From 24d5837864f2623851ddd9e8f93a657c9dd84cab Mon Sep 17 00:00:00 2001 From: aJream <563664686@qq.com> Date: Fri, 17 Sep 2021 11:02:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E7=AB=A0=EF=BC=9A?= =?UTF-8?q?=E8=B4=A8=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db.json | 2 +- public/service-worker.js.map | 2 +- .../\350\264\250\346\225\260.md" | 38 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 "source/_posts/\347\256\227\346\263\225/\350\264\250\346\225\260.md" diff --git a/db.json b/db.json index 38075aa1..34c5435a 100644 --- a/db.json +++ b/db.json @@ -1 +1 @@ -{"meta":{"version":1,"warehouse":"4.0.0"},"models":{"Asset":[{"_id":"themes/butterfly/source/css/copyright.css","path":"css/copyright.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/background.css","path":"css/background.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/custom.css","path":"css/custom.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/icon.css","path":"css/icon.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/index.min.css","path":"css/index.min.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/mouse.css","path":"css/mouse.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/index.styl","path":"css/index.styl","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/side.css","path":"css/side.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/var.styl","path":"css/var.styl","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/JetBrainsMono-Medium.woff2","path":"fonts/JetBrainsMono-Medium.woff2","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/ZhuZiAWan.woff2","path":"fonts/ZhuZiAWan.woff2","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/ajLangMan.ttf","path":"fonts/ajLangMan.ttf","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/loving.ttf","path":"fonts/loving.ttf","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/shishangblack.ttf","path":"fonts/shishangblack.ttf","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/01.jpg","path":"img/01.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/02.jpg","path":"img/02.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/404.jpg","path":"img/404.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/34.jpg","path":"img/34.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/articles.png","path":"img/articles.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/algolia.svg","path":"img/algolia.svg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/articles2.png","path":"img/articles2.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/avatar1.jpg","path":"img/avatar1.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/avatar2.jpg","path":"img/avatar2.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/banner.png","path":"img/banner.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/blissed01.jpg","path":"img/blissed01.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/categories.png","path":"img/categories.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/favicon.png","path":"img/favicon.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/friend_404.gif","path":"img/friend_404.gif","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/huiji.png","path":"img/huiji.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/mybatis.png","path":"img/mybatis.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/mybatis1.png","path":"img/mybatis1.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/pcbimg.png","path":"img/pcbimg.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/pcbimg2.png","path":"img/pcbimg2.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/plane.png","path":"img/plane.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/plane1.png","path":"img/plane1.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/main.js","path":"js/main.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/tw_cn.js","path":"js/tw_cn.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/utils.js","path":"js/utils.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/LICENSE","path":"live2d-widget/LICENSE","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/README.md","path":"live2d-widget/README.md","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/autoload.js","path":"live2d-widget/autoload.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/live2d.min.js","path":"live2d-widget/live2d.min.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.js","path":"live2d-widget/waifu-tips.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.json","path":"live2d-widget/waifu-tips.json","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/waifu.css","path":"live2d-widget/waifu.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/AppStarting.ani","path":"mouse/AppStarting.ani","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Arrow.cur","path":"mouse/Arrow.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Cross.cur","path":"mouse/Cross.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Hand.cur","path":"mouse/Hand.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Handwriting.cur","path":"mouse/Handwriting.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Help.cur","path":"mouse/Help.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/IBeam.cur","path":"mouse/IBeam.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/NO.cur","path":"mouse/NO.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeAll.cur","path":"mouse/SizeAll.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeNESW.cur","path":"mouse/SizeNESW.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeNWSE.cur","path":"mouse/SizeNWSE.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeNS.cur","path":"mouse/SizeNS.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeWE.cur","path":"mouse/SizeWE.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/UpArrow.cur","path":"mouse/UpArrow.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Wait.ani","path":"mouse/Wait.ani","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/search/algolia.js","path":"js/search/algolia.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/search/local-search.js","path":"js/search/local-search.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-1.png","path":"live2d-widget/assets/screenshot-1.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-2.png","path":"live2d-widget/assets/screenshot-2.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-3.png","path":"live2d-widget/assets/screenshot-3.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/demo/demo.html","path":"live2d-widget/demo/demo.html","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/demo/login.html","path":"live2d-widget/demo/login.html","modified":0,"renderable":1}],"Cache":[{"_id":"source/link/index.md","hash":"b523cebc9646f9e71dced10c15286446b5ec05f9","modified":1629177172461},{"_id":"source/_data/link.yml","hash":"ac42ffbb8934c77eaaa451aaaee0e997edfbef39","modified":1630841898931},{"_id":"source/about/index.md","hash":"2cbe86fa0f91f1ec4e9e9f50d2622ba6fb334c4b","modified":1629470228214},{"_id":"source/categories/index.md","hash":"85594ad9ae83a45a6fd1f1bb812d1c348f0e0d04","modified":1629199613838},{"_id":"source/notes/index.md","hash":"cd1eca6299ad86555ea010220c23a582e1d1d689","modified":1630456117545},{"_id":"source/poems/index.md","hash":"4a8f9bd31f5b9f30a41e5c168294ff8006cfe2c0","modified":1630495317769},{"_id":"source/music/index.md","hash":"18c94b16f0d06e13daf5574fba544e4beee1888e","modified":1631436778221},{"_id":"source/tags/index.md","hash":"7d44ddaac9bce3ef3d0747699ad557271b21e2e2","modified":1629177106636},{"_id":"source/web-links/index.md","hash":"ed38c45ed78d5406a00f6c1c76fe9f98b03e6403","modified":1630461912181},{"_id":"source/_posts/51单片机/51单片机学习笔记(一).md","hash":"baa93866bf0443a9487ceadf241ea3e6191c644a","modified":1631277953371},{"_id":"source/_posts/51单片机/51单片机学习笔记(七).md","hash":"5ab18e79564dd583a3ff8a2bf3d9003ff862e599","modified":1631277926554},{"_id":"source/_posts/51单片机/51单片机学习笔记(三).md","hash":"b77cb8e4e58ba7affe617b7147b698d826fc3b2d","modified":1631277931265},{"_id":"source/_posts/51单片机/51单片机学习笔记(九).md","hash":"0fb139f3222ec409ca81bb298615c252bb79f6c4","modified":1631277912950},{"_id":"source/_posts/51单片机/51单片机学习笔记(二).md","hash":"c2df9e5ec32fba24ee910a0d2c234e5deafe1f76","modified":1631277906012},{"_id":"source/_posts/51单片机/51单片机学习笔记(五).md","hash":"6429fd26f9e6ab42720347db1992a5fbc97d2f5f","modified":1631277942618},{"_id":"source/manifest.json","hash":"c06ff8a12fb81cfe47befc9d3941aacfae49ddff","modified":1631611477371},{"_id":"source/_posts/51单片机/51单片机学习笔记(八).md","hash":"b5b3eac1a371a6578b27d7852d2fa707dac5c42d","modified":1631277895652},{"_id":"source/_posts/Hexo/Hexo添加百度统计分析.md","hash":"7b581d3c1adea4dee8d792cf805bdcc9f875b4c4","modified":1630841698490},{"_id":"source/_posts/51单片机/51单片机学习笔记(六).md","hash":"bed0d77a7c301947aa63f142220c27b865207a18","modified":1631277919484},{"_id":"source/_posts/51单片机/51单片机学习笔记(四).md","hash":"121d5f350d59cbce348b1d505f5a39a62036f1e4","modified":1631277936194},{"_id":"source/_posts/Hexo/butterfly主题配置.md","hash":"642f98cc33649a81685e660a687364d9f0e4d338","modified":1631806136062},{"_id":"source/_posts/Java面试/Java垃圾回收算法.md","hash":"c3b1129a2cbc588037e603bf4d9fba0116158a03","modified":1631347297796},{"_id":"source/_posts/Hexo/github Actions自动部署.md","hash":"ffb2b229f96e063815bcb43494cd15ccac5d9a5b","modified":1631806189622},{"_id":"source/_posts/Maven/1-maven基本概念.md","hash":"b721b4c3648699ce80e70b838843d2413027b96a","modified":1631242769812},{"_id":"source/_posts/Hexo/标签外挂.md","hash":"6187523670c82eb272c8f40fdee46b4467e3a6c1","modified":1631806122688},{"_id":"source/_posts/Maven/3-maven+IDEA创建webapp.md","hash":"24ef7a6e34687f49bf679e80d4e3cb75b8b98f2d","modified":1631242813294},{"_id":"source/_posts/Java面试/java面试题.md","hash":"47f820465287ffb7f9cf553b0be66aacf31c5d43","modified":1630720868705},{"_id":"source/_posts/MySQL/4-MySQL事务.md","hash":"9762f26985acb72368eb4a7ea8b6c6eba05c5faf","modified":1631106363208},{"_id":"source/_posts/MySQL/1-数据库认识.md","hash":"69ef32bfb52656fc614d7bb34317c45bed43af3a","modified":1631242870897},{"_id":"source/_posts/MySQL/2-MySQL基础.md","hash":"2b07760222f7dc7d425384768eda92c763fa4ac7","modified":1631106379369},{"_id":"source/_posts/MySQL/5-索引.md","hash":"54678b3e8b7c0424178ba9bc356dbed1b535187e","modified":1631106357536},{"_id":"source/_posts/MySQL/3-数据管理.md","hash":"526b0eec8b3c745a9543b8d5802cd52c4e9bda2b","modified":1631242915065},{"_id":"source/_posts/Spring/1-spring框架介绍.md","hash":"4c12fd3d702fbdde0e3c78008d84883b134df0b1","modified":1631243841676},{"_id":"source/_posts/MySQL/6-权限-备份.md","hash":"47af43803556a42b669ca47bba616a7853203e1d","modified":1631106351506},{"_id":"source/_posts/Spring/10-注解实现自动装配.md","hash":"696a8643c333620c9eafd1f3dc9b6ac54ce84639","modified":1631243823959},{"_id":"source/_posts/Spring/12-静态代理.md","hash":"ce877429ec005160d28d09a33bf1f16e364aa935","modified":1631243812129},{"_id":"source/_posts/MySQL/7-JDBC.md","hash":"305788488fc48dbb74c73517ba15c2eed0eec6e7","modified":1631242990248},{"_id":"source/_posts/Spring/11-使用注解开发.md","hash":"8adf385ed6593347df06ec5dd923c6c7f1d345bd","modified":1631243818357},{"_id":"source/_posts/Spring/2-SpringIOC.md","hash":"e3a00f743c9a3dd346761d2a800c5998de95d8b8","modified":1631243889108},{"_id":"source/_posts/Spring/14-springAOP.md","hash":"fe9e27a79ea54944c5895bfdfce143c3186b47f9","modified":1631243778937},{"_id":"source/_posts/Spring/3-ioc控制反转.md","hash":"5d6dc86b0671061d8a92a8bc50e8632c279dfbe5","modified":1631243873109},{"_id":"source/_posts/Spring/13-动态代理.md","hash":"47b762cb652146dd068fe76f4080ce9decaf8cff","modified":1631243800037},{"_id":"source/_posts/Spring/7-spring容器注入.md","hash":"1e72586920062aeb3c7c39258470b53555578d47","modified":1631243912070},{"_id":"source/_posts/Spring/5-ioc创建对象方式.md","hash":"724e4210c04ef41ce123f7026151aec89a4e66dd","modified":1631243900605},{"_id":"source/_posts/Spring/4-HelloSpring程序.md","hash":"bbe75d0e4aa6cb9ed065b771be37c69392227620","modified":1631243881840},{"_id":"source/_posts/Spring/6-spring配置说明.md","hash":"4c06ce7bab72f94b6e46774f1773997b4d76f090","modified":1631243904710},{"_id":"source/_posts/Spring/8-命名空间和作用域.md","hash":"9401035baf7cd2bd15f608b48730e70ed02a1640","modified":1631243833986},{"_id":"source/_posts/Mybatis/mybatis关联查询(四).md","hash":"b6c78531032c3896d7c1f20086e27416f4c7731e","modified":1630314135636},{"_id":"source/_posts/Spring/9-bean自动装配.md","hash":"b7608f63c78c92f5df4342d582c77c22ca3b1980","modified":1631243829012},{"_id":"source/_posts/Mybatis/mybatis动态sql(七).md","hash":"5b5cccaf4b436fa4516447fb86afe1b34eb638c1","modified":1630248324576},{"_id":"source/_posts/Mybatis/mybatis延迟加载(六).md","hash":"6c245ee1328b1492dfcbf53448f4b7d400f0fdd6","modified":1630331393564},{"_id":"source/_posts/Mybatis/mybatis的Mapper详解(三).md","hash":"9d1cf80be6c69ef5f3f6fe840940d50453e8e09c","modified":1630248305560},{"_id":"source/_posts/Mybatis/mybatis简单使用(二).md","hash":"a5588151a36c8144206562e2a711af39ac9ba893","modified":1630248299444},{"_id":"source/_posts/Mybatis/mybatis简单介绍与使用(一).md","hash":"2a1b3741be4325b72ce72b46c99943c7988e564d","modified":1630248286760},{"_id":"source/_posts/Mybatis/mybatis逆向工程(五).md","hash":"80fadcf88c09c5e70a0e43a7883c4a905176efaf","modified":1630248315143},{"_id":"source/_posts/esp8266/0-Arduino配置esp8266开发环境.md","hash":"1ae7dabb617ed7af563ab4fc971fa2ff3346bc7b","modified":1631346932130},{"_id":"source/_posts/esp8266/1-接入点模式-无线终端模式.md","hash":"d6b50a08f00138a630722ab9cefa7804e28bdac6","modified":1631243986998},{"_id":"source/_posts/esp8266/2-网络服务器.md","hash":"a145f1fbefcc624a4080db65fb41cffef88f575f","modified":1631243997136},{"_id":"source/_posts/esp8266/3-闪存文件系统.md","hash":"665075ac149ce26c7ae45cc1632d5b5247155fb7","modified":1631346924239},{"_id":"source/_posts/esp8266/5-Stream数据流.md","hash":"2059838c8bbbd5ae5338438f4e59189252a82a5b","modified":1631346878317},{"_id":"source/_posts/esp8266/4-NodeMCU作为客户端使用.md","hash":"32dd90d0f0a098bf778d9df7c338094137a509b3","modified":1631346862484},{"_id":"source/_posts/esp8266/8-OTA上传代码.md","hash":"c135b08812c2e0646f859d788d9bf45aa9250b91","modified":1631346901650},{"_id":"source/_posts/esp8266/MQTT篇(一)-认识MQTT与基本操作.md","hash":"92e8dafacbff0a5f3c229d36984db24054b6cbcc","modified":1631346808088},{"_id":"source/_posts/esp8266/7-多任务执行.md","hash":"fe76210a836b07eebc72e106657bd31e9c1211f0","modified":1631346890150},{"_id":"source/_posts/esp8266/6-WiFi配网.md","hash":"d422ebb4c9ded7c59d0dd7511b19e3d88bcae27a","modified":1631346886084},{"_id":"source/_posts/esp8266/MQTT篇(三)-主题.md","hash":"a058bd9738090459b257442d7b58abb338ed4f72","modified":1630248886346},{"_id":"source/_posts/esp8266/MQTT篇(二)-发布、订阅、取消订阅.md","hash":"d19e95d87777a83680af4eabe4b46f9139907966","modified":1630248882648},{"_id":"source/_posts/SpringMVC/springMVC(二)HelloMVC.md","hash":"f8e912976546f72f47b177bf009c4047c35d9f91","modified":1630838038666},{"_id":"source/_posts/SpringMVC/springMVC(三)使用注解开发.md","hash":"7e2d4406b2a227668dec925352eaf45ec626b550","modified":1630838043284},{"_id":"source/_posts/SpringMVC/springMVC(一)快速入门.md","hash":"47df9392752049d5b2e0a1d89f39784a70694268","modified":1630838027417},{"_id":"source/_posts/SpringMVC/springMVC(四)Controller与restful.md","hash":"0c393bcd4bd730b37abdc8a0bd284732cc479384","modified":1630838047415},{"_id":"source/_posts/SpringMVC/springMVC(六)数据处理.md","hash":"ecfa53b64252b8a777f8a12b80535836366ebb4d","modified":1630838056597},{"_id":"source/_posts/javaEE/3-ServletRequest对象.md","hash":"12b2f1398cbf09df5b3d75cf4e6635367791655c","modified":1631243407722},{"_id":"source/_posts/SpringMVC/springMVC(五)结果跳转方式.md","hash":"4ae1e8827eafb617ef1747747666088b1c643675","modified":1630838051953},{"_id":"source/_posts/javaEE/2-Servlet生命周期.md","hash":"48a296346a3c6e86079a15682d8d935f52f0fb07","modified":1631243355138},{"_id":"source/_posts/javaEE/1-Servlet.md","hash":"c7270b3dccaa9fbac4bb7a880da86fd54eedb857","modified":1631243338332},{"_id":"source/_posts/javaEE/IDEA配置Tomcat.md","hash":"57335e93f1c10a450b18f759c99607235c5fdf1d","modified":1631243461852},{"_id":"source/_posts/javaEE/http.md","hash":"cd4021433ae1ac20e4dd6f1f9a40316f0c69b01d","modified":1630677215404},{"_id":"source/_posts/javaEE/4-请求转发.md","hash":"51c6485851dfc005dcc187e62a5c18147059b6ef","modified":1631243421610},{"_id":"source/_posts/javaEE/Tomcat基础.md","hash":"de9787f7bf7a2f41fcc6a2f4df6fef969e73d1f0","modified":1630671686646},{"_id":"source/_posts/javaSE/10-装箱与拆箱.md","hash":"b70216ab8145f5f31a461c09e0981606264097f2","modified":1630251080436},{"_id":"source/_posts/javaSE/1-类与对象.md","hash":"cd815d389512cc1ea6ee5596c3abccea4160df9b","modified":1631243135295},{"_id":"source/_posts/javaSE/11-数字与字符串相互转换.md","hash":"4f1b133b565eae5977800de91d6eb8266621f826","modified":1630251084118},{"_id":"source/_posts/javaSE/12-字符串操作.md","hash":"658bf9a88b0d6dd97e82c1e11ae37a9b9f84caae","modified":1630251087482},{"_id":"source/_posts/javaSE/14-接口与继承.md","hash":"4198da5d1d74a47f2fa49b09141631318db96c23","modified":1630251095517},{"_id":"source/_posts/javaSE/13-日期.md","hash":"ae9aaed18184ef39f36ef26152111df220172c65","modified":1630251091919},{"_id":"source/_posts/javaSE/15-异常处理.md","hash":"2ec30d857c147fdacc21419556f48f9e78fa671c","modified":1630251098885},{"_id":"source/_posts/javaSE/16-文件IO.md","hash":"bcd6604db1fa6f0c96b03a07b2b9bdfbbcc28d9f","modified":1630251102266},{"_id":"source/_posts/javaSE/17-容器.md","hash":"3cb845c57d11ceb506bf2eb3c321d8a7a56abb07","modified":1630251116883},{"_id":"source/_posts/javaSE/17-集合框架.md","hash":"9d1e5fd4a725c71ad667d51d2dc638152be6c3a6","modified":1630251105529},{"_id":"source/_posts/javaSE/18-泛型.md","hash":"286cc161aff3cf7577cf60d64eeaef6aa861eea7","modified":1630251121869},{"_id":"source/_posts/javaSE/2.2-访问修饰符.md","hash":"b01d48e5e16a4016638f2ae7f977152b30fc6ef7","modified":1630251038950},{"_id":"source/_posts/javaSE/19-多线程系列.md","hash":"74a44f6b72f52971feefc14012f22f1eed5dd49f","modified":1630251125689},{"_id":"source/_posts/javaSE/2.1-类和对象的属性初始化.md","hash":"64bb8acbe190e10d7a13e568920368ff3bd4c94b","modified":1630251034836},{"_id":"source/_posts/javaSE/21-注解.md","hash":"948bf85840b582a6d5333bf6dd56a010e5324c49","modified":1630251134568},{"_id":"source/_posts/javaSE/3.1-数据类型.md","hash":"3a380b105bf644c6905b3d32a4382dd495c26f0b","modified":1630251043790},{"_id":"source/_posts/javaSE/3.2-数组.md","hash":"2cf0a2ca05c216f0f83842ce9654d81a73d86321","modified":1630251047232},{"_id":"source/_posts/javaSE/20-反射机制.md","hash":"115db83be1b9e6a83c1b005757dc670a5a49b8d2","modified":1630251130453},{"_id":"source/_posts/javaSE/4-变量.md","hash":"abc5bd6ca9258e7b14570e03c66d1520cb59d483","modified":1630251050603},{"_id":"source/_posts/javaSE/5-操作符.md","hash":"1ec84eddc3febdebc9540dd7c0718361ec2ec8a3","modified":1630251053821},{"_id":"source/_posts/javaSE/6-输入语句.md","hash":"e2995606574b6e504d1202ff261537f53f95718d","modified":1630251057135},{"_id":"source/_posts/javaSE/7-条件语句.md","hash":"e43786948b7a87e74960b8459b30eea0bfd81df5","modified":1630312166817},{"_id":"source/_posts/javaSE/8-循环语句.md","hash":"7797c7d3f5730b6507c9e83b2b15720320075b00","modified":1630251071601},{"_id":"source/_posts/win10/win10任务栏透明.md","hash":"daa7cba90b6693dddc8f378234a96d31d1a90c28","modified":1631024957913},{"_id":"source/_posts/javaSE/9-package包.md","hash":"e71ddfcdc911f3455aa514c0458974a2a9c84bb7","modified":1630251074685},{"_id":"source/_posts/win10/win10创建系统还原节点.md","hash":"d88958fa074d91459ad9df888b6dc46ab0fbe3ab","modified":1631112351921},{"_id":"source/_posts/win10/win10主题美化.md","hash":"834b1c6baa1802570ca526f151a8ab8dd4e27f51","modified":1630834352290},{"_id":"source/_posts/stm32/MDK_Keil卸载已经安装的pack.md","hash":"50804d67534ad7f9b9214ff801d2a1bc3579b271","modified":1631278107623},{"_id":"source/_posts/stm32/stm32标准外设库介绍.md","hash":"49b69166a379f02c46ca6d5bce694e707600e515","modified":1631417937863},{"_id":"source/_posts/stm32/keil新建库函数版本的project.md","hash":"bc5ec136879bdfbd73eeadc5d26fd83502fcef36","modified":1631285270686},{"_id":"source/_posts/stm32/stm32点亮led灯程序.md","hash":"47e58ef7caa184c1e68294bbfd6c3fc02cfdd9eb","modified":1631411594761},{"_id":"source/_posts/杂七杂八/gitee图床配置.md","hash":"71a1ffcb7d8384734563cff8f2acdbcf9c99ed04","modified":1631536238795},{"_id":"source/_posts/网络安全/古典加密算法.md","hash":"c07a4af38074ebd217f0926bfc94ff12d06e020d","modified":1631802714376},{"_id":"source/_posts/网络通信/计算机网络的产生.md","hash":"a8774c3ea7a7502a6f19109ac7a9c46adce82e81","modified":1631498410634},{"_id":"source/_posts/电磁场/矢量场.md","hash":"071499e078c59f02d2813c38988c15dc6f53ad34","modified":1631092459444},{"_id":"source/_posts/网络通信/网络协议.md","hash":"821f48c6c4d29fd76d7ce17b784bd6e63b7d492c","modified":1631498054362},{"_id":"themes/butterfly/README_CN.md","hash":"459d6f3200863021bee1fe72a719aef236fb4090","modified":1627578032000},{"_id":"themes/butterfly/LICENSE","hash":"1128f8f91104ba9ef98d37eea6523a888dcfa5de","modified":1627578032000},{"_id":"themes/butterfly/package.json","hash":"c18b118422237b964063874a099945b79573feca","modified":1627578032000},{"_id":"themes/butterfly/languages/en.yml","hash":"cd333235ff1648a6bf58dfafc81f2c57672a15a5","modified":1627578032000},{"_id":"themes/butterfly/languages/default.yml","hash":"7ca673fb629ea74f5ba5e75b4f0f95248cfb5090","modified":1627578032000},{"_id":"themes/butterfly/languages/zh-CN.yml","hash":"741e522b2387f94764a73844e7b084cc7e927c54","modified":1627578032000},{"_id":"themes/butterfly/_config.yml.bak","hash":"8609065adff452353410b14cced654a0a7430914","modified":1629765958049},{"_id":"themes/butterfly/languages/zh-TW.yml","hash":"79a50c40d9f5463f1fa42aa870ac6b8b84540412","modified":1627578032000},{"_id":"themes/butterfly/layout/archive.pug","hash":"bd62286afb64a51c97e800c5945620d51605d5fa","modified":1627578032000},{"_id":"themes/butterfly/layout/category.pug","hash":"60c1b795b6e227b5dd81963b51d29d1b81d0bf49","modified":1627578032000},{"_id":"themes/butterfly/layout/index.pug","hash":"201b6f40da9a60ef76e4f51c5dd1440114dcb303","modified":1630593644328},{"_id":"themes/butterfly/layout/page.pug","hash":"82aa988527a11835e7ac86ce4f23b8cd20014dfa","modified":1627578032000},{"_id":"themes/butterfly/layout/tag.pug","hash":"0440f42569df2676273c026a92384fa7729bc4e9","modified":1627578032000},{"_id":"themes/butterfly/README.md","hash":"cedd13fcd8c75a68742265dd8eced4087e940ffd","modified":1627578032000},{"_id":"themes/butterfly/scripts/year.js","hash":"a72100c58fc177fc1ec123ece67bb216a32911b7","modified":1629471168674},{"_id":"themes/butterfly/.github/stale.yml","hash":"05a55a87fa7f122c59683e41c8b2e37e79f688f0","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/404.pug","hash":"7d378e328a53cc99d5acc9682dce53f5eb61537d","modified":1627578032000},{"_id":"themes/butterfly/.github/ISSUE_TEMPLATE/feature_request.md","hash":"f6867a2f0417fe89a0f2008730ee19dd38422021","modified":1627578032000},{"_id":"themes/butterfly/layout/post.pug","hash":"8d398c8925182699d9f2b9f1b727f06228488312","modified":1627578032000},{"_id":"themes/butterfly/.github/ISSUE_TEMPLATE/custom.md","hash":"eff495eb1584cf4586e33c76e8b2fa6a469a179b","modified":1627578032000},{"_id":"themes/butterfly/.github/ISSUE_TEMPLATE/bug_report.md","hash":"476802922b774b679225102ac30a9d9183394701","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/footer.pug","hash":"02390a5b6ae1f57497b22ba2e6be9f13cfb7acac","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/additional-js.pug","hash":"4156224c47bfc2482281ac4e4df701c30476ff00","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head.pug","hash":"1377952022ee0a9eaa7a2fd1098f1571efc468d9","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/layout.pug","hash":"6f2608c4d93d3d10ae6b2cd7f8918f303f024321","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/sidebar.pug","hash":"8dafc2dcd8c33f70a546fee443f0b6d80b3cd243","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/pagination.pug","hash":"0b80f04950bd0fe5e6c4e7b7559adf4d0ce28436","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/rightside.pug","hash":"2d0453adf92a3fd3466cf0793f14685d17b8b51d","modified":1627578032000},{"_id":"themes/butterfly/scripts/events/init.js","hash":"018aa446265fe627301b1d53d7cba4f4ff1960ac","modified":1627578032000},{"_id":"themes/butterfly/scripts/events/404.js","hash":"83cd7f73225ccad123afbd526ce1834eb1eb6a6d","modified":1627578032000},{"_id":"themes/butterfly/scripts/filters/post_lazyload.js","hash":"4cc2d517195c8779471d326ada09f9371cbad4dd","modified":1627578032000},{"_id":"themes/butterfly/scripts/events/welcome.js","hash":"d575137c8779e50422c2416f4d0832fdea346ee6","modified":1627578032000},{"_id":"themes/butterfly/scripts/filters/random_cover.js","hash":"9821872007cf57efae4b728dc575ef9d004547bb","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/aside_archives.js","hash":"2ec66513d5322f185d2071acc052978ba9415a8e","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/page.js","hash":"c6611d97087c51845cb1ab4821696a62fa33daeb","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/inject_head_js.js","hash":"65f2442e04c4defd16e7c1e67701d3bb41d9577a","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/aside_categories.js","hash":"e00efdb5d02bc5c6eb4159e498af69fa61a7dbb9","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/button.js","hash":"b816ded1451f28c7c54151ffe6c259b110253ae3","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/related_post.js","hash":"54b9324e3506dcc9c9991ef5e11e37e66e21594f","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/hide.js","hash":"f33858ffb9e88191e644796e11d2f901eb332308","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/gallery.js","hash":"94826ea6bcc4d2304199adae12c4e2b272caf529","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/inlineImg.js","hash":"a43ee2c7871bdd93cb6beb804429e404570f7929","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/mermaid.js","hash":"35f073021db93699fcac9ef351e26c59c31aadf7","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/label.js","hash":"03b2afef41d02bd1045c89578a02402c28356006","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/tabs.js","hash":"6c6e415623d0fd39da016d9e353bb4f5cca444f5","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/note.js","hash":"c16c6eb058af2b36bcd583b2591076c7ebdd51ad","modified":1627578032000},{"_id":"themes/butterfly/source/css/copyright.css","hash":"40f1b088daeaca4fdfe81e77024dd863aa5e7c1c","modified":1629187161084},{"_id":"themes/butterfly/source/css/background.css","hash":"24beaed4bf1eb5f2157743f0805e209e320374c0","modified":1631178433645},{"_id":"themes/butterfly/source/css/icon.css","hash":"d93c1e972e3569c9a08fedf3f54a1fe28f2b036f","modified":1631435783049},{"_id":"themes/butterfly/source/css/custom.css","hash":"662ddd3067ea440c511faeb917a5a0a49f38dda6","modified":1631535464054},{"_id":"themes/butterfly/source/css/index.styl","hash":"861998e4ac67a59529a8245a9130d68f826c9c12","modified":1627578032000},{"_id":"themes/butterfly/source/css/index.min.css","hash":"706e9fbfd4eace05e57d1d414b610f72a267480e","modified":1631105037871},{"_id":"themes/butterfly/.github/workflows/publish.yml","hash":"05857c2f265246d8de00e31037f2720709540c09","modified":1627578032000},{"_id":"themes/butterfly/source/css/mouse.css","hash":"a2d961444557cd7a72dc1d8e26c6f051e916249e","modified":1630154901393},{"_id":"themes/butterfly/source/css/side.css","hash":"5bce6787fa34f53e43a96a5c2700b510fb137b0d","modified":1630154791413},{"_id":"themes/butterfly/source/css/var.styl","hash":"40c3f64422205a24e68ce1ad8fe8163f24fdd525","modified":1627578032000},{"_id":"themes/butterfly/source/fonts/JetBrainsMono-Medium.woff2","hash":"f31bdf436d68c69503e96e7ca691a90ffa4918a8","modified":1630301034059},{"_id":"themes/butterfly/source/img/404.jpg","hash":"fb4489bc1d30c93d28f7332158c1c6c1416148de","modified":1627578032000},{"_id":"themes/butterfly/source/img/articles.png","hash":"f6654630e0f6f18497475b34e607f63a79f09140","modified":1630247726978},{"_id":"themes/butterfly/source/img/articles2.png","hash":"44f2e90e928bf1ae3fd91a85b70b5b1d07a91519","modified":1630247920935},{"_id":"themes/butterfly/source/img/algolia.svg","hash":"ec119560b382b2624e00144ae01c137186e91621","modified":1627578032000},{"_id":"themes/butterfly/source/img/avatar1.jpg","hash":"466d1b7cd0573e57e7db294c5f63c55ee6ca4296","modified":1630311556297},{"_id":"themes/butterfly/source/img/favicon.png","hash":"3cf89864b4f6c9b532522a4d260a2e887971c92d","modified":1627578032000},{"_id":"themes/butterfly/source/img/avatar2.jpg","hash":"4a7143ede9561ace432c2ac963b406428b296756","modified":1630313302374},{"_id":"themes/butterfly/source/img/mybatis.png","hash":"1e685804796630baba037812e3beab62936e1227","modified":1630246612746},{"_id":"themes/butterfly/source/img/huiji.png","hash":"f33b6927a0124ce7ad10638b96ee3be9b55c555e","modified":1630246963906},{"_id":"themes/butterfly/source/img/plane.png","hash":"fcd0b34a638838dcae8cbf17f34c51f973a0c02b","modified":1628982336217},{"_id":"themes/butterfly/source/img/pcbimg.png","hash":"72afcf1951d00c206d235facf6e09d16802c6ba1","modified":1630248671451},{"_id":"themes/butterfly/source/img/pcbimg2.png","hash":"511c4a15ad0f382adcbd3e1ff86dca3ead5d2df1","modified":1630487183495},{"_id":"themes/butterfly/source/img/mybatis1.png","hash":"d40a9abf2799be2e830868845e7287e884d8688d","modified":1630248123122},{"_id":"themes/butterfly/source/js/main.js","hash":"8ef2821ceb92d81aa0f8c02ee932f094adcafb2b","modified":1627578032000},{"_id":"themes/butterfly/source/js/tw_cn.js","hash":"00053ce73210274b3679f42607edef1206eebc68","modified":1627578032000},{"_id":"themes/butterfly/source/js/utils.js","hash":"8319b59c26ce8cd2b0ae7d030c4912215148fa92","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/autoload.js","hash":"e4364dd681c0a6d5bf64b50b94c3fa656303aa26","modified":1630559474816},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.json","hash":"cc68124da17a8547f26ebca0c0596d20dea37238","modified":1629353672566},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.js","hash":"2684e315f9aeb882055f541f943c959bd3afbe34","modified":1629353672566},{"_id":"themes/butterfly/source/img/plane1.png","hash":"c5713f3d92ddcd45d2b9a494f78b0f5b0e4a7466","modified":1628982320662},{"_id":"themes/butterfly/source/live2d-widget/waifu.css","hash":"003ddb4a25b804a9ce99c6f365142fa382dd0e75","modified":1629355252618},{"_id":"themes/butterfly/source/live2d-widget/LICENSE","hash":"1eba7caf09a39110ad2f542e3ed8700d1a69c6d3","modified":1629353672556},{"_id":"themes/butterfly/source/mouse/Cross.cur","hash":"6d6d6f975164d58db643a84ed238375fcbdac98d","modified":1564159338221},{"_id":"themes/butterfly/source/mouse/Handwriting.cur","hash":"195c938abc7f3e6d1f2e6301c46ba1e42ff99a4f","modified":1564159347886},{"_id":"themes/butterfly/source/live2d-widget/README.md","hash":"665054726a60b77a8f5e5b96527b8f1a4f166e30","modified":1629353672556},{"_id":"themes/butterfly/source/mouse/Help.cur","hash":"bf59506b7a65e2a5bb8c07b9b3c8daa6b90b0b7d","modified":1564159364356},{"_id":"themes/butterfly/source/mouse/Arrow.cur","hash":"1ab6471c728291cf66ba813b09135d48a06e74c3","modified":1564159324877},{"_id":"themes/butterfly/source/mouse/IBeam.cur","hash":"c96a55e806892deb9c1866c1aa33f98627d6dadf","modified":1564159375581},{"_id":"themes/butterfly/source/mouse/SizeAll.cur","hash":"b8ccca4354a8c11d61b7281818004933bdc32e01","modified":1564159388444},{"_id":"themes/butterfly/source/mouse/AppStarting.ani","hash":"e334f5489e63167adbd44f1116ffcfc72e84f718","modified":1564159318638},{"_id":"themes/butterfly/source/mouse/Hand.cur","hash":"46d6d4e0a8a79e054df35bcee1a53bb3c4b1ec8b","modified":1564159343139},{"_id":"themes/butterfly/source/mouse/SizeWE.cur","hash":"915ae8029726e0cf7b8f523fff059a96148cfc5c","modified":1564159419424},{"_id":"themes/butterfly/source/mouse/SizeNWSE.cur","hash":"25666d52fb83373286dc0b815387be41a52a26ac","modified":1564159413351},{"_id":"themes/butterfly/source/mouse/NO.cur","hash":"709b03659cd3be203e9d1c449d675e866847b957","modified":1564159381269},{"_id":"themes/butterfly/source/mouse/SizeNESW.cur","hash":"253f4e4c0da9b85757596c09187a79204fd9f07b","modified":1564159398663},{"_id":"themes/butterfly/layout/includes/head/Open_Graph.pug","hash":"6c41f49a3e682067533dd9384e6e4511fc3a1349","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/google_adsense.pug","hash":"95a37e92b39c44bcbea4be7e29ddb3921c5b8220","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/config_site.pug","hash":"889ef16fa34a39e5533bc170e62f20f3450cc522","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/analytics.pug","hash":"90d01b88d0f406d00184960b1afe9230aec2ebe6","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/config.pug","hash":"4def0aab9e2172ad1f29abd1535d8e08ff23aa0b","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/preconnect.pug","hash":"e55f8bdb876d5429a908498db1307b94094c0d06","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/pwa.pug","hash":"3d492cfe645d37c94d30512e0b230b0a09913148","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/noscript.pug","hash":"d16ad2ee0ff5751fd7f8a5ce1b83935518674977","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/site_verification.pug","hash":"e2e8d681f183f00ce5ee239c42d2e36b3744daad","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/nav.pug","hash":"c205b9fd72b2fe19e6d15c5b5ab0fb38c653032e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/social.pug","hash":"0d953e51d04a9294a64153c89c20f491a9ec42d4","modified":1627578032000},{"_id":"themes/butterfly/source/mouse/SizeNS.cur","hash":"4c02aa53675e758f61de6e13a5cf764b41e129f5","modified":1564159406614},{"_id":"themes/butterfly/layout/includes/loading/loading-js.pug","hash":"4cfcf0100e37ce91864703cd44f1cb99cb5493ea","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/loading/loading.pug","hash":"5276937fbcceb9d62879dc47be880cd469a27349","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/index.pug","hash":"65fa23680af0daf64930a399c2f2ca37809a8149","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/menu_item.pug","hash":"24370508ee87f14418e8f06e9d79ad8c52a342c4","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/post-info.pug","hash":"92f81a437c9db49f7ebcf608bc09488ecdb55a21","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/categories.pug","hash":"1f30952fed73dec21b42e2e30b7fe2e84618d2e4","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/default-page.pug","hash":"dbec869c62135695495703a29ad7655e9965d461","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/flink.pug","hash":"b53a2d4f9c37b375a4446d2273dcfb7712d91b3e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/tags.pug","hash":"93d4ebc7dc8228c7a10ddeb5a553d0dcdabbe145","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/mixins/article-sort.pug","hash":"bfd20a845dc4ac8d30792692954f3f404bb25ea0","modified":1629471437777},{"_id":"themes/butterfly/layout/includes/post/reward.pug","hash":"5b404356f311d2ee36478291ca3553210867b738","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/mixins/post-ui.pug","hash":"4c3c5cb69b3aead8c232cb0fbc251929f28aad75","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/aplayer.pug","hash":"292646dfab135973b09f0fa9e3931e83da2ed30e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/pangu.pug","hash":"d5fec7dedc52ab23865fb4db002755e9bdaadc9f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/post/post-copyright - 副本.pug","hash":"88e3b611b03149665e4113cfa39595c1a3fca7e5","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/post/post-copyright.pug","hash":"2ed4573caa447dd9eac1bf859a09e3618f7dc2c8","modified":1629186546309},{"_id":"themes/butterfly/layout/includes/third-party/prismjs.pug","hash":"1fbecfd299068f90d727f0c8c65e2a792fa6e3e2","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_ad.pug","hash":"60dc48a7b5d89c2a49123c3fc5893ab9c57dd225","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_archives.pug","hash":"86897010fe71503e239887fd8f6a4f5851737be9","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/effect.pug","hash":"b9d54a01d7c2a7a183cb7209e99430ce7fea1fe3","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_announcement.pug","hash":"3d8e3706a056389176f55dd21956aabc78046761","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/pjax.pug","hash":"933cb710d2dbcea25c6426a57c6f49d2f48b792c","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_bottom_self.pug","hash":"13dc8ce922e2e2332fe6ad5856ebb5dbf9ea4444","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_categories.pug","hash":"d1a416d0a8a7916d0b1a41d73adc66f8c811e493","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_newest_comment.pug","hash":"27afd2274bd5f2cbbf1bad9f0afe2b2b72c213ca","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/subtitle.pug","hash":"d50e5c22cd6bc3c378bc581918136746cfa3447f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_post_toc.pug","hash":"ae9336bf31cdad08ff586ead4295912a96563c76","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_tags.pug","hash":"438aea3e713ed16b7559b9a80a9c5ec0221263df","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_top_self.pug","hash":"ae67c6d4130a6c075058a9c1faea1648bcc6f83e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_author.pug","hash":"0366c658cdcff839aa1df2e2d252a03a53fd427e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_recent_post.pug","hash":"9c1229af6ab48961021886882c473514101fba21","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/index.pug","hash":"b5525891f6affd02c1ea3b2327c026882efe428b","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_webinfo.pug","hash":"0612aaee878f33ea8d3da0293c7dc3b6cd871466","modified":1627578032000},{"_id":"themes/butterfly/source/css/_global/function.styl","hash":"eda47f3e807a466ba8275627ea936c5100c43818","modified":1627578032000},{"_id":"themes/butterfly/source/css/_global/index.styl","hash":"e211efbd1952d4b1b881287aa43423133c1d166e","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/highlight.styl","hash":"85e72c70a0cef29e40be1968f5d23b06c6f8e3aa","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/theme.styl","hash":"fa7a4c1685f391d60ed863e869b9604b59746c27","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/404.styl","hash":"b0488ceacde74af139d66c8db5cb36cc21737b9b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/archives.styl","hash":"6874adc2e276443f354bbe50d0072e9bec37243c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/categories.styl","hash":"e554549f0a0ae85362f0b0e8687981741f486f6b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/common.styl","hash":"97fec1e814f88237862f4d800a35362b802f6625","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/tags.styl","hash":"9a881c031f463c486bd25248c2814fd09f97892b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/flink.styl","hash":"2cc49d3f6a6beb9f7bff93e292f88aa5681da1d0","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/homepage.styl","hash":"7c4152162a03aa8331a783df5695e4ebbb816a8c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_mode/darkmode.styl","hash":"4e629f510b73f998ab208b739c5bd7dcd168d1a7","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/chat.styl","hash":"29f48f9370f245e6e575b5836bccf47eb5688d8b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_mode/readmode.styl","hash":"f59a9a0059d5261251bdd6de45aa97dd2d11e633","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/aside.styl","hash":"7feb755ca7c22da36bbad11e74ecd95fdcf3b879","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/categoryBar.styl","hash":"7e31831eef19bd1957b5b6b5a957c64f20ef703d","modified":1630592669826},{"_id":"themes/butterfly/source/css/_layout/footer.styl","hash":"dd8cdf639ba2b726437c77fa7aa8d5edbabe8f9b","modified":1627578032000},{"_id":"themes/butterfly/source/mouse/UpArrow.cur","hash":"2e65c3437b457ddf6429a096d5921ea962703843","modified":1564159425136},{"_id":"themes/butterfly/source/css/_layout/comments.styl","hash":"f1b63892baafa48ab872bc79671d57aafd511f6c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/head.styl","hash":"98235fcda3b87ad6f7e91eafbed94d0d6ae847ca","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/loading.styl","hash":"7d18a7be9cfea65091de3ef00014063d2d649912","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/pagination.styl","hash":"90fe01c968696a9f791cb2b84fca621cbbb56f47","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/post - 副本.styl","hash":"d748951d9fbcd04dda839085af78b01b8fa04cba","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/relatedposts.styl","hash":"0551c5893d1589a3d17ce161e50ecb1d724cc6e8","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/rightside.styl","hash":"7a072589e6097dbe942783131964f2372fdf1eb6","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/post.styl","hash":"72536fe1f905d0719896fd38c91d20040e3bc1c9","modified":1629187118515},{"_id":"themes/butterfly/source/css/_layout/reward.styl","hash":"ea1ba40dd5954c2ed718a126336fb7f94da4e66f","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/hexo.styl","hash":"d0386ba6d8d63afc72b9673e8f3e89df6446ffc2","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/third-party.styl","hash":"978c397d0966eaf9e6e2afd13866f8f4900b509f","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/sidebar.styl","hash":"2c5fb77c448ce0a734040c8ce532b28fed688899","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/button.styl","hash":"1c3f9d7efc3b9dfcfa8926a1132d0c44ffc7d4b2","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/label.styl","hash":"f741e85295ce15c70a6027ec15a542636dd5dcca","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/hide.styl","hash":"21964fdd6d74ffbea519418bab65024aee5f3736","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/inlineImg.styl","hash":"df9d405c33a9a68946b530410f64096bcb72560c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/gallery.styl","hash":"53ecae272e16223a436c497abbf25dd5f0fc4aaa","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/note.styl","hash":"86fee274a62f7f034547342930f445c47378eb55","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/tabs.styl","hash":"1756791581c0ec51cb03353a09dac4778d944349","modified":1627578032000},{"_id":"themes/butterfly/source/css/_search/index.styl","hash":"f168f5c669978f633abe118cdcc4a12cfc883c01","modified":1627578032000},{"_id":"themes/butterfly/source/css/_search/local-search.styl","hash":"6befe4c51b86d0c1de130beeecad9e28d6442713","modified":1627578032000},{"_id":"themes/butterfly/source/css/_third-party/normalize.min.css","hash":"2c18a1c9604af475b4749def8f1959df88d8b276","modified":1627578032000},{"_id":"themes/butterfly/source/css/_search/algolia.styl","hash":"917e0e399e117217184ca63d3eb5c4843bcccf7b","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-1.png","hash":"4bd4d97a1fcaa5deb3cea1c0a102a895a15af32f","modified":1629353672556},{"_id":"themes/butterfly/source/js/search/local-search.js","hash":"459e2541afda483916d16fce4aaa56b41bcd42ba","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/fb.pug","hash":"7848ec58c6ec03243abf80a3b22b4dc10f3edf53","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/disqus.pug","hash":"d85c3737b5c9548553a78b757a7698df126a52cf","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/index.pug","hash":"e3bf847553515174f6085df982f0623e9783db7a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/valine.pug","hash":"bba9871f446c10ffcc8fa9023f5a2eb701a86bae","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/waline.pug","hash":"400ce038548d6f9ddb486150c724c87b6923a88b","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/chatra.pug","hash":"481cd5053bafb1a19f623554a27d3aa077ea59c3","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/twikoo.pug","hash":"ef1b2b5b980d6aeaa5d06b97d1afc9644b155a16","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/index.pug","hash":"3f05f8311ae559d768ee3d0925e84ed767c314d3","modified":1627578032000},{"_id":"themes/butterfly/source/js/search/algolia.js","hash":"65b45e61586f7e66c3f338370bfd9daadd71a4b7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/daovoice.pug","hash":"cfe63e7d26a6665df6aa32ca90868ad48e05ec04","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/crisp.pug","hash":"76634112c64023177260d1317ae39cef2a68e35f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/gitter.pug","hash":"d1d2474420bf4edc2e43ccdff6f92b8b082143df","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/tidio.pug","hash":"24a926756c2300b9c561aaab6bd3a71fdd16e16d","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/disqusjs.pug","hash":"2e52c64e89f16267596a8465841dd46f51820982","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/disqus.pug","hash":"a111407fdcafcf1099e26ffa69786f8822c5d9fb","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/facebook_comments.pug","hash":"c46a932257212f82e4a9974fbbc5de8878c8b383","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/gitalk.pug","hash":"0b7571919e8ad51285deda56a1868fccf8c563d7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/index.pug","hash":"da9813f8dc0d388869c15413cf056012cfb69e1a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/js.pug","hash":"bafb3d5710824caa59a56017afb058fd2b4eac65","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/livere.pug","hash":"52ea8aa26b84d3ad38ae28cdf0f163e9ca8dced7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/utterances.pug","hash":"b871ea208e36398b4d668db9a9a0b61c79415381","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/twikoo.pug","hash":"16378d8646ea3f4ac99c18f0296dd85b13f9d775","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/index.pug","hash":"b8ae5fd7d74e1edcef21f5004fc96147e064d219","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/valine.pug","hash":"2b45fe09d5b591dca156b76dae99981f8d8e1c61","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/katex.pug","hash":"f9b00ead54573ba6e6eb33481588af144aab648d","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/mermaid.pug","hash":"3f3a3cd8bea2103dedd754f767aca5cb84d5f586","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/waline.pug","hash":"36f3c603d2a2ecddaa6d2675a89d76ad94968f72","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/index.pug","hash":"f6506ccfd1ce994b9e53aa95588d0b6dbad11411","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/mathjax.pug","hash":"a47d8f9f593091cc91192c0c49deaa2c0d2317fd","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/disqus-comment.pug","hash":"b443d6b16baf3ea250041342cc0361a42a412b7f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/github-issues.pug","hash":"34088a15655704d12e9b1807b47b3f6a860c9eec","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/search/algolia.pug","hash":"d8f59e94eafc669c49349561dc5bbea3915aecb7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/twikoo-comment.pug","hash":"cb38ffe911023092a90a28f2ba8317a92b22cd0c","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/search/index.pug","hash":"da3b9437d061ee68dbc383057db5c73034c49605","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/add-this.pug","hash":"2980f1889226ca981aa23b8eb1853fde26dcf89a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/search/local-search.pug","hash":"613280d61b8ab9612014ec016ae3d3698d36fd1a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/valine.pug","hash":"59b4c26a827ace5a54855881d199977103ff6f50","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/index.pug","hash":"4c4a9c15215ae8ac5eadb0e086b278f76db9ee92","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/waline.pug","hash":"a2bc2601b7e0ae5caf1fc51a07390562d928620a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/addtoany.pug","hash":"309f51bc5302e72fc469d54c577fbcfe57fb07a8","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/share-js.pug","hash":"006acc91ce25fc7c7d778ca043e970f57dc46b83","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/demo/demo.html","hash":"a26089ebcf2606b5b38e71a1e68d64f9344cac1a","modified":1629353672556},{"_id":"themes/butterfly/source/css/_highlight/prismjs/index.styl","hash":"e0e7065124ef0d99f8322a47bc47838982e04ad0","modified":1630509500097},{"_id":"themes/butterfly/source/css/_highlight/prismjs/line-number.styl","hash":"8970cc1916c982b64a1478792b2822d1d31e276d","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/highlight/diff.styl","hash":"8c0726fb8d9a497d2f900b0be2845efaa68e3d87","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/highlight/index.styl","hash":"89cbcc8e087788ecec18b5fa58710afacdb7d080","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/demo/login.html","hash":"1fd46a102ccf4e89a483b85ef4bd79a563563836","modified":1629353672556},{"_id":"themes/butterfly/source/img/friend_404.gif","hash":"8d2d0ebef70a8eb07329f57e645889b0e420fa48","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/prismjs/diff.styl","hash":"5972c61f5125068cbe0af279a0c93a54847fdc3b","modified":1627578032000},{"_id":"themes/butterfly/source/mouse/Wait.ani","hash":"5a7278ded553b61a248ec549b9964b7e2930cecf","modified":1564159429851},{"_id":"themes/butterfly/source/live2d-widget/live2d.min.js","hash":"61a09b54a472b4d0541b7c50afb3dfdd172e062f","modified":1629353672566},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-3.png","hash":"f89c6fdf44008e7d3c7a1aafe65dc39b212edb6c","modified":1629353672556},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-2.png","hash":"76b844653136184c573b3632e12296e008a7ff5c","modified":1629353672556},{"_id":"themes/butterfly/source/img/02.jpg","hash":"52276fbc7427eaf3facebf1e7cf9d99dad1664e8","modified":1630244205652},{"_id":"themes/butterfly/source/img/banner.png","hash":"e0b6d5d65d72bdfeb1b1bcfd1af340088d50fdfb","modified":1629110564595},{"_id":"themes/butterfly/source/img/01.jpg","hash":"455aff642579a632713b90c58edcd89f1ea01aef","modified":1629129110293},{"_id":"themes/butterfly/source/img/34.jpg","hash":"10b2ddfbe7f03c4513ace7a2855a47f907478d55","modified":1630244370887},{"_id":"themes/butterfly/source/img/blissed01.jpg","hash":"932db4b0cb8a297a4f9c1fd48af59183db742584","modified":1630244182581},{"_id":"themes/butterfly/source/img/categories.png","hash":"1829d3667c9fb36d729057a2fb26f12b662c86e4","modified":1629111056814},{"_id":"themes/butterfly/source/fonts/shishangblack.ttf","hash":"b5dda0beb2618829e41553d0dc656afbc78af523","modified":1630200705629},{"_id":"themes/butterfly/source/fonts/ajLangMan.ttf","hash":"c79e63ca78049f8273c0de7b5c65898c1dd8557f","modified":1629378758891},{"_id":"themes/butterfly/source/fonts/loving.ttf","hash":"786ab7558a8a2cd5de1ac71a1c6c19bfeff17e95","modified":1630200693598},{"_id":"themes/butterfly/source/fonts/ZhuZiAWan.woff2","hash":"a8512c6d5d70f5fc098a58a01a0ad720aa211c2a","modified":1631094979211},{"_id":"public/manifest.json","hash":"e5653018c633c03092c691caf2b17921c60fe633","modified":1631633856066},{"_id":"public/search.xml","hash":"6c5cefd2b310b7656e3e459e7b0e7403496dd38a","modified":1631633856066},{"_id":"public/404.html","hash":"58e406587db3548d33d3249886af5dfd2184989b","modified":1631633856066},{"_id":"public/about/index.html","hash":"f996050c1b8d596c9e3eb62b844982d16c705354","modified":1631633856066},{"_id":"public/link/index.html","hash":"3b9a75e10a18bd16eeb4cc20b268ccdc1154cb20","modified":1631633856066},{"_id":"public/music/index.html","hash":"06b0e469a43d9c6106c6c747688d2c70ffaed9df","modified":1631633856066},{"_id":"public/categories/index.html","hash":"118251e34b7c66b9fffb5c11e190b8a502340816","modified":1631633856066},{"_id":"public/notes/index.html","hash":"2a8ebaaa7b34885bf46975ce47271c15376a2c4b","modified":1631633856066},{"_id":"public/tags/index.html","hash":"f27805e42397220cc3da0d6c75998ebde260ffe1","modified":1631633856066},{"_id":"public/poems/index.html","hash":"43bf0ab218e30404765cf9c6856609221af24068","modified":1631633856066},{"_id":"public/posts/ee4b2b4d.html","hash":"8d72917cfce0212eb47acd0741fa39f2b6f463c9","modified":1631633856066},{"_id":"public/web-links/index.html","hash":"d535c4b5620fad3790ba92c6ee0641255846c328","modified":1631633856066},{"_id":"public/posts/88ad757.html","hash":"e2c6b65699d1ae25bb7326977c592940610bea1c","modified":1631633856066},{"_id":"public/posts/8b75069d.html","hash":"1d88cc36293dcdc8aaed858046a09f9ca9299bf0","modified":1631633856066},{"_id":"public/posts/1cd3002f.html","hash":"4233127e9c2c23aec8e5a96562a10729d76dcdd3","modified":1631633856066},{"_id":"public/posts/7dc1e032.html","hash":"dd8bdb3613e5ada905a2303e4f0a20dd70bc3ad6","modified":1631633856066},{"_id":"public/posts/f8dae6c5.html","hash":"1958ecfe80d2ab434268ebb6d92eab79269b420f","modified":1631633856066},{"_id":"public/posts/1294a4bd.html","hash":"48dd307b9e356f06fa0f34705d134ff0404f7573","modified":1631633856066},{"_id":"public/posts/190b30be.html","hash":"0a6066123a28e82d23c126603dd94029f5b7a860","modified":1631633856066},{"_id":"public/posts/a4c109bd.html","hash":"8b62e61ea8c3d924a7899598ee3e90b082b94702","modified":1631633856066},{"_id":"public/posts/54667693.html","hash":"6eb8e590c7c95e6b7a4b1c41f67e71e395af019a","modified":1631633856066},{"_id":"public/posts/401491fc.html","hash":"3f36bb80cfce6539e641a434a813461e982300ab","modified":1631633856066},{"_id":"public/posts/3de0f4a2.html","hash":"70d982cabab55ff24be5dee1668a0064b36427db","modified":1631633856066},{"_id":"public/posts/54a4218.html","hash":"3f61abfc0f2e9c6f1e5818e88f8144c60b815c8c","modified":1631633856066},{"_id":"public/posts/446faab7.html","hash":"f9bb5a58323e604583e6972b4327cd007b1068d7","modified":1631633856066},{"_id":"public/posts/ecec7d3f.html","hash":"f7a6f90bd0bd011ecff7a1ae78e8b4a5237c0999","modified":1631633856066},{"_id":"public/posts/1eb33081.html","hash":"752450393018314ba436dd83174188cec39a814c","modified":1631633856066},{"_id":"public/posts/57ac54fe.html","hash":"840bf899083f71fbaad61d0924b1483082f2a7e7","modified":1631633856066},{"_id":"public/posts/b8426075.html","hash":"04afceb2bf3a32a62bec0e50d7834ac94c2bc864","modified":1631633856066},{"_id":"public/posts/a4682555.html","hash":"c74ff055138550853d2b10c4c2786f86f2f1b90a","modified":1631633856066},{"_id":"public/posts/890c8bdb.html","hash":"5a095ce108ea77900ff3f1dede8b8c3e9aa110cc","modified":1631633856066},{"_id":"public/posts/c8eb4af9.html","hash":"1015211fc45492a3cc728dc820119856fe769505","modified":1631633856066},{"_id":"public/posts/dc7f8ae1.html","hash":"dd08a553296eca1cd503fafdfec55b2de37191fc","modified":1631633856066},{"_id":"public/posts/6e81c43e.html","hash":"9dff365ca5e2ca3018c9e24b4c1883a47dca57a8","modified":1631633856066},{"_id":"public/posts/3b32a20d.html","hash":"0cde59bc14487f5e042ef11ebab2939318469ad9","modified":1631633856066},{"_id":"public/posts/81ecdf4a.html","hash":"2a19a3f511e557417d497c8fcdf6550f0c0774aa","modified":1631633856066},{"_id":"public/posts/ba43a8dc.html","hash":"6212939774785dc57ce61487108b65aca471a997","modified":1631633856066},{"_id":"public/posts/f218ca42.html","hash":"15848001e001d4294889a0192f352aedf901a53a","modified":1631633856066},{"_id":"public/posts/ab69c7ec.html","hash":"0464e8ae29728e7233613df9f1b7b32ac471213f","modified":1631633856066},{"_id":"public/posts/78903d87.html","hash":"1abf47c7de03d672c2b052b0fcd47d89985cdc5e","modified":1631633856066},{"_id":"public/posts/17528481.html","hash":"635f9377b2af5bbd1c10086cb56b7af7fbfa374f","modified":1631633856066},{"_id":"public/posts/b836e65.html","hash":"899c0ee9cc3a85afb1f7a468acf90110a378639f","modified":1631633856066},{"_id":"public/posts/c2d1d169.html","hash":"ab4f3b09ca9fe2d5cec97d4dc79aa3848185ecb1","modified":1631633856066},{"_id":"public/posts/5dd9c9e3.html","hash":"f30ded54e22b1fceead36f2e2a5dd5c3e019a3ca","modified":1631633856066},{"_id":"public/posts/c4d68191.html","hash":"18d8daa44348d26765dc9c712348e31f8a0be08b","modified":1631633856066},{"_id":"public/posts/c27dd6ec.html","hash":"cf0de529f9f84bc11beb034ea11b1776af6f8365","modified":1631633856066},{"_id":"public/posts/383e39e3.html","hash":"021dfc87842ab104e9c0d30865925e1c267933c2","modified":1631633856066},{"_id":"public/posts/46cd19c8.html","hash":"026c49121699da6c7fc2f088b31ee4c37199361f","modified":1631633856066},{"_id":"public/posts/9e0a8624.html","hash":"f374ce85021f993616b98cbcb0f732d92fa56668","modified":1631633856066},{"_id":"public/posts/894e18ee.html","hash":"4d0f636e99cfc36b3744679343a46adc32da2cce","modified":1631633856066},{"_id":"public/posts/adfcabaa.html","hash":"35c9c2f7c7e6f3ac48d5c6d9fa5ca600027d413f","modified":1631633856066},{"_id":"public/posts/73ba8569.html","hash":"986c05360940fbd4b8bd2bde272c0d5f3a665f75","modified":1631633856066},{"_id":"public/posts/1b6a7930.html","hash":"b500d5c03f2cd0eb749287a9cd3358e45dc2c756","modified":1631633856066},{"_id":"public/posts/c25015c5.html","hash":"1d683c51cbc3bafb2ef0c86de378eac6a540cddb","modified":1631633856066},{"_id":"public/posts/baad5185.html","hash":"38344a849c2cb8105f352d24a5f50e8a13a6520b","modified":1631633856066},{"_id":"public/posts/2d7f83ef.html","hash":"671a3d3c537097a02c6433796db424da86beb86f","modified":1631633856066},{"_id":"public/posts/d150c3f1.html","hash":"c1c8faaadbfa1dbc74f6aa851f67ba90aca2817e","modified":1631633856066},{"_id":"public/posts/2080031a.html","hash":"8c6c1e6d3d1d6168b715f00829bfdbec4d40e569","modified":1631633856066},{"_id":"public/posts/27153554.html","hash":"fd9c69f99d550f23f4022eaa14a422278c7f5d76","modified":1631633856066},{"_id":"public/posts/4dc0a7a8.html","hash":"9e5ce406873762f90541189bcb77718568d4be69","modified":1631633856066},{"_id":"public/posts/89cf4bca.html","hash":"01069c939864e7b1e4c2952faa2d42c2d571e684","modified":1631633856066},{"_id":"public/posts/a1a1eb5.html","hash":"57cd8daacd3ff4d4e0a132d13a0a52475f261d4c","modified":1631633856066},{"_id":"public/posts/be39d4e8.html","hash":"bee976b1bf03fac9080b30a939c8f1fa9c775246","modified":1631633856066},{"_id":"public/posts/26c04051.html","hash":"45f844b331497a4c93752c8ee47dea07c82aef5f","modified":1631633856066},{"_id":"public/posts/38b3e585.html","hash":"b182f51537f07fcf9e8c1207d33d187cdc0c1985","modified":1631633856066},{"_id":"public/posts/39f00355.html","hash":"2e4539e7c805a0449b314be125dbd82343fe575e","modified":1631633856066},{"_id":"public/posts/f68104ec.html","hash":"fba07f1b3374a419d3fc34f3519bc77e1a262950","modified":1631633856066},{"_id":"public/posts/3feecb5e.html","hash":"8bd533a97d3d7c3f351745c0894f5fa623d17377","modified":1631633856066},{"_id":"public/posts/7f2244ba.html","hash":"b6733cbc60db2edcd6e54a1eea52afc59e88c911","modified":1631633856066},{"_id":"public/posts/d8642fb9.html","hash":"ca62d808348a78f38227ac0e69cdb939c67f0204","modified":1631633856066},{"_id":"public/posts/3fb3b16d.html","hash":"813ec5ece159676140fec6d31021763f0765f117","modified":1631633856066},{"_id":"public/posts/d959138b.html","hash":"7b572164e53e13a48e1c6329abf6549fe17ca910","modified":1631633856066},{"_id":"public/posts/59e405f3.html","hash":"fd6e165bb6b6c2bab7183b91e71c41a85991f7e7","modified":1631633856066},{"_id":"public/posts/752d4bdb.html","hash":"0dcb911913475c61dd4df6b86effa2e2fb50bd06","modified":1631633856066},{"_id":"public/posts/3afd7950.html","hash":"6154496d361851690e16f347f75a63f997634b04","modified":1631633856066},{"_id":"public/posts/530cfffd.html","hash":"d5f009bd083d94f216f9084caf80bc75b2e38983","modified":1631633856066},{"_id":"public/posts/3f4cddeb.html","hash":"dc99bec7ffa43bcde7b8d315dac042fb0c9573b5","modified":1631633856066},{"_id":"public/posts/1a57c6b6.html","hash":"7d290b2df71f7a7bc589c62f80b2584412b55fc9","modified":1631633856066},{"_id":"public/posts/d29e4d17.html","hash":"62dac627c7b813865909b4da59e9c2d78aa27348","modified":1631633856066},{"_id":"public/posts/2a3cf99e.html","hash":"656b6ce8849f4497a362d04801c2cc8c01ceabe5","modified":1631633856066},{"_id":"public/posts/683d1a78.html","hash":"1b0c01572589a576b1a062bab76674f6cffe4e02","modified":1631633856066},{"_id":"public/posts/d709dda.html","hash":"ce9df1dfc884deeaa312e3eaf9293051e57c7c9b","modified":1631633856066},{"_id":"public/posts/39aef30f.html","hash":"2ab0b61161357d59b42e9282294f8a67690decb2","modified":1631633856066},{"_id":"public/posts/b9bb0bd5.html","hash":"8882192a09fa15f9bb58649c084b61d387dd8587","modified":1631633856066},{"_id":"public/posts/af3c95f0.html","hash":"9d89b51dbee638317273dd94a302bc503f2c9f63","modified":1631633856066},{"_id":"public/posts/bdfc8ea1.html","hash":"3ab6e1c5ca7b13791351c1abba88b9d002919a6f","modified":1631633856066},{"_id":"public/posts/79c0e989.html","hash":"9c9e72fc2d4d01597e00352faadea3d10d85103b","modified":1631633856066},{"_id":"public/posts/52e2375.html","hash":"d03264d3de06bdb89eb0c0a33db3475b1cd1b569","modified":1631633856066},{"_id":"public/posts/374aa28c.html","hash":"301d8688b2c88c5f1ffa1fe435e8732aeb10e65a","modified":1631633856066},{"_id":"public/posts/1a9b2152.html","hash":"be3bcfb2c1a8c4b5e3820e24b61a379e5412f86a","modified":1631633856066},{"_id":"public/posts/4609d38b.html","hash":"907eff716d4015e52f359d9201de10527b8c2593","modified":1631633856066},{"_id":"public/posts/e7ba7594.html","hash":"f8a5d307059fa0d275228864c3eceecccaaf763d","modified":1631633856066},{"_id":"public/posts/345d8e17.html","hash":"cf57b589bc3f9745b92e1d75f66941e6f2da1bce","modified":1631633856066},{"_id":"public/posts/170e1590.html","hash":"6d4845a6fb049215d8f320892387e4993d4deb95","modified":1631633856066},{"_id":"public/posts/c9f91dab.html","hash":"75fe236ecffedfdb0d9ca19dc445b3b7815bbed6","modified":1631633856066},{"_id":"public/posts/c5d59ae.html","hash":"0dbfcdd4761f1020e8bc6a9ed4435fabc2f59353","modified":1631633856066},{"_id":"public/posts/e0bbd322.html","hash":"cf01d97e38f8aa6198854d446c0e0b57fb90bf7f","modified":1631633856066},{"_id":"public/posts/c0626013.html","hash":"928bd3cbdb1292f4a25b0b142367ef3800cfd261","modified":1631633856066},{"_id":"public/posts/9dc8e4af.html","hash":"c912829f4478f39e6d1a2c7de26a6329b0c50dea","modified":1631633856066},{"_id":"public/posts/4f0e7b50.html","hash":"ec56a4d038522edb58d9fef79e3b4c8313de3c8f","modified":1631633856066},{"_id":"public/posts/f5c95c.html","hash":"3878be1111006dfab543cf265d933e10ec3d9b1d","modified":1631633856066},{"_id":"public/posts/ab09b476.html","hash":"53c83764ad4aa611379f4d980349e2b580178ca2","modified":1631633856066},{"_id":"public/posts/b3cec184.html","hash":"625e998ef8e74a970ea998a04baee33b5b59c2b2","modified":1631633856066},{"_id":"public/posts/3aac01ca.html","hash":"58b9c1ace0157361d90d5fd95050a228c4185ad9","modified":1631633856066},{"_id":"public/posts/5e64b19a.html","hash":"0930d5ed8b1496d2da1a7aefea2f79ff04f83a3e","modified":1631633856066},{"_id":"public/posts/38b36403.html","hash":"4c158a9b59335c192ddf512b514c7f59d49cd635","modified":1631633856066},{"_id":"public/posts/94566522.html","hash":"46947a24b755977cb6287517dbc6883f2e29c9aa","modified":1631633856066},{"_id":"public/posts/b54f36d8.html","hash":"15857f756806f2b7b5f68a1dd7db8e18c22cfbda","modified":1631633856066},{"_id":"public/posts/753dfa3e.html","hash":"516213b5208a2e98096a6851eabf579083b5595e","modified":1631633856066},{"_id":"public/posts/63036c3c.html","hash":"d65b1b4675a9a7569fce7f534106de633dfccdc1","modified":1631633856066},{"_id":"public/posts/8a45e6a3.html","hash":"e2e873dfab3d9757d86f87bb78e2571524dd1cd4","modified":1631633856066},{"_id":"public/posts/68f2c599.html","hash":"9d9f42efb5244e6bd19232c59025fe165523ad35","modified":1631633856066},{"_id":"public/posts/c63fec2a.html","hash":"74ec8ba9f848be9fab846e232034eefdd3593ab3","modified":1631633856066},{"_id":"public/posts/52da8eea.html","hash":"c3002fea62a2bacaf72433b2a04c35be31d8f780","modified":1631633856066},{"_id":"public/posts/51a2ab89.html","hash":"312e59ffe8193710fd26a3c9de94dcb91c54cbd9","modified":1631633856066},{"_id":"public/posts/4515cdfc.html","hash":"7055bc1ea114a702b30ba2fb498c476788cb988f","modified":1631633856066},{"_id":"public/posts/52b44ec7.html","hash":"dda522576c0659a99584a597af2ad9302a5cd863","modified":1631633856066},{"_id":"public/archives/index.html","hash":"d63799f0f677d7c83c84d6ca525367a0fef7f0a2","modified":1631633856066},{"_id":"public/archives/page/2/index.html","hash":"97b487f9dc7a26267090e0e0aa0926cd6554f47e","modified":1631633856066},{"_id":"public/archives/page/3/index.html","hash":"9ec1c1771331caff2e752d663843fbf47d9aeec5","modified":1631633856066},{"_id":"public/archives/page/4/index.html","hash":"81fdd2d50278aad738dc6e08854a8383daa1e507","modified":1631633856066},{"_id":"public/archives/page/5/index.html","hash":"9c3714a0b98b49d94468153530deefd13bbae5d7","modified":1631633856066},{"_id":"public/archives/page/6/index.html","hash":"bfb7543d14a274c93e96c69a4bd50f9c32b8d083","modified":1631633856066},{"_id":"public/archives/page/7/index.html","hash":"68b36bd61b4d4964e7d0964862fe9a40dabee5fd","modified":1631633856066},{"_id":"public/archives/page/8/index.html","hash":"5134324fd8056f4ca2a5a89c972dd9a0e1a1bb1f","modified":1631633856066},{"_id":"public/archives/page/9/index.html","hash":"3e0953b96746f3fdefac23c46bc7b92eb9de82c6","modified":1631633856066},{"_id":"public/archives/page/10/index.html","hash":"4bb0eee002c1c0be43e00351f8768cea330393e6","modified":1631633856066},{"_id":"public/archives/page/11/index.html","hash":"243cdbe7c3510b0e67fa837103ef8895bcabcb75","modified":1631633856066},{"_id":"public/archives/2021/index.html","hash":"aed4dff4f7eabba3f598b8366d22cdce7e303aa7","modified":1631633856066},{"_id":"public/archives/2021/page/2/index.html","hash":"8d92a763323c7e1e651817967dbfc04fa349f287","modified":1631633856066},{"_id":"public/archives/2021/page/3/index.html","hash":"0ef2d816fd7dafe857cab558945632323566c9d2","modified":1631633856066},{"_id":"public/archives/2021/page/4/index.html","hash":"6e0523af12e62414b2ee617e6788ceddd292763f","modified":1631633856066},{"_id":"public/archives/2021/page/5/index.html","hash":"9368fa0707de91160d921d3fe526dc8464fe8e9c","modified":1631633856066},{"_id":"public/archives/2021/page/6/index.html","hash":"9096c0a325a74f257d889d8c59811f0570c677e8","modified":1631633856066},{"_id":"public/archives/2021/page/7/index.html","hash":"c145880d3746c753a7568cc9450eca5f141c4e3b","modified":1631633856066},{"_id":"public/archives/2021/page/8/index.html","hash":"13c3c686fb758361db669f05bbc1f796dd72aef0","modified":1631633856066},{"_id":"public/archives/2021/page/9/index.html","hash":"f2c9de409a465c904cef11b8e8eb9c24f4bf44bd","modified":1631633856066},{"_id":"public/archives/2021/page/10/index.html","hash":"fa8c30c321463828963cc63a12ab2b52221bee87","modified":1631633856066},{"_id":"public/archives/2021/page/11/index.html","hash":"a5ed686ffc885a3c30e66532b5a3605bd19dd18a","modified":1631633856066},{"_id":"public/archives/2021/02/page/2/index.html","hash":"d7ba72d3ec237b08868f6da77d0cc9adf03b2a16","modified":1631633856066},{"_id":"public/archives/2021/02/index.html","hash":"0ccee5fa890a0afe16f6fc70269d8bfeb2a802e7","modified":1631633856066},{"_id":"public/archives/2021/02/page/3/index.html","hash":"6234cc5534859f5cb625375d19c50197af44d080","modified":1631633856066},{"_id":"public/archives/2021/04/index.html","hash":"3d2cfbfe660476858f18df5d3ef6b5dbcbc9e5ea","modified":1631633856066},{"_id":"public/archives/2021/05/index.html","hash":"c7e87b5418e60e671c08b3215b11ded7c2ff0bea","modified":1631633856066},{"_id":"public/archives/2021/06/index.html","hash":"520feefe0bbf26618f87443db4f82dacbd1dc588","modified":1631633856066},{"_id":"public/archives/2021/07/index.html","hash":"fae42ce426a1e6b6131ef1e5d978522bada35050","modified":1631633856066},{"_id":"public/archives/2021/07/page/2/index.html","hash":"bf4259c12b5736065d91e88d600cd57a173d8a59","modified":1631633856066},{"_id":"public/archives/2021/08/index.html","hash":"4378387e033c92810936da38d83ef5e7ec2f80e9","modified":1631633856066},{"_id":"public/archives/2021/08/page/2/index.html","hash":"ea75b482085cea17b3c33d90fad21b5c0bdfac51","modified":1631633856066},{"_id":"public/archives/2021/08/page/3/index.html","hash":"9b820787545efebbdef7ad12916ba4e5a9e26c00","modified":1631633856066},{"_id":"public/archives/2021/09/index.html","hash":"1be93dd46feb5ef99ef3c0c1eddbf56150638306","modified":1631633856066},{"_id":"public/archives/2021/09/page/2/index.html","hash":"e0909354dfada8ba1400bba0cdf5793de875df85","modified":1631633856066},{"_id":"public/categories/硬件学习/index.html","hash":"6a513b2a67c272a769bb6878eba5665fc167c0de","modified":1631633856066},{"_id":"public/categories/硬件学习/page/2/index.html","hash":"e9be192a35daa186ca3887a5a4543ac72b8bf49b","modified":1631633856066},{"_id":"public/categories/硬件学习/page/3/index.html","hash":"035bc3639d947d047fdded4dee18e3aef129947f","modified":1631633856066},{"_id":"public/categories/硬件学习/51单片机/index.html","hash":"f7af8ae920e8f5322dc8b98535279adb7d767a8b","modified":1631633856066},{"_id":"public/categories/Hexo/index.html","hash":"41825b4793ba33e31f0a795bfb423cf8739a724e","modified":1631633856066},{"_id":"public/categories/java/index.html","hash":"7c969a7ded506e20259de863de0cc68cd87478dc","modified":1631633856066},{"_id":"public/categories/java/page/2/index.html","hash":"3742eb83c1d8810af0a92ad62995dd1d2c254c50","modified":1631633856066},{"_id":"public/categories/java/page/3/index.html","hash":"db4f3020f34b31b06ed847037ebf4ab51ccf3a53","modified":1631633856066},{"_id":"public/categories/java/page/4/index.html","hash":"366da5957d294c82e392ade1a3db201b442feafb","modified":1631633856066},{"_id":"public/categories/java/page/5/index.html","hash":"2527d33a961f30c84ddf60e9a4409661f982d90e","modified":1631633856066},{"_id":"public/categories/java/page/6/index.html","hash":"bbaf8f7136f06bf796c5b158409e2375e1d48f9c","modified":1631633856066},{"_id":"public/categories/java/page/7/index.html","hash":"9a250026ace06bf39aedfb8590f4b9a9f07c1857","modified":1631633856066},{"_id":"public/categories/java/spring/index.html","hash":"4f42cea914f8cfcee7d4ea4958981d9b8c316067","modified":1631633856066},{"_id":"public/categories/java/spring/page/2/index.html","hash":"d731115057fcfc84b3043a452c0394e38dbc1fe8","modified":1631633856066},{"_id":"public/categories/java/java面试/index.html","hash":"ddb813a2c2a140287becfd75254266db228e3517","modified":1631633856066},{"_id":"public/categories/java/Maven/index.html","hash":"02e396a361742fe8247c33d00b8316c26d729025","modified":1631633856066},{"_id":"public/categories/java/Mybatis/index.html","hash":"52163b938fc3e972740512866de07be376ad2727","modified":1631633856066},{"_id":"public/categories/java/MySQL/index.html","hash":"0cc0c99dae4e1e2a82d9409fac856bd6aff0f754","modified":1631633856066},{"_id":"public/categories/硬件学习/esp8266/index.html","hash":"0bfa22368fd6da59eaabbb2a8c12ba1c89583c3a","modified":1631633856066},{"_id":"public/categories/硬件学习/物联网开发/index.html","hash":"2ed8a94c707cef5e188d594250a312b56c9a101d","modified":1631633856066},{"_id":"public/categories/java/springMVC/index.html","hash":"29c74e1664fb0ff84d5b3ca0f807f4c27965da58","modified":1631633856066},{"_id":"public/categories/java/javaEE/index.html","hash":"94402c66d020a587e916f3239a158cf0e08ccdd3","modified":1631633856066},{"_id":"public/categories/java/javaSE/index.html","hash":"3b5ea63a00cb05334ca9666cc3b07c736b6893e1","modified":1631633856066},{"_id":"public/categories/java/javaSE/page/2/index.html","hash":"c2a80c3af933daaf85b6703e2e3ea4da3cdac239","modified":1631633856066},{"_id":"public/categories/java/javaSE/page/3/index.html","hash":"690743c4d0cd73ecb6589e283232de9386db51b0","modified":1631633856066},{"_id":"public/categories/win10/index.html","hash":"9e86a6126de017f8134c2ec648b2ac36fb149814","modified":1631633856066},{"_id":"public/categories/硬件学习/stm32/index.html","hash":"1a67a33a38c3b171c7351568abb12cae1babc303","modified":1631633856066},{"_id":"public/categories/杂七杂八/index.html","hash":"438a4d1d0df06c74c0ed89264052ce97e913fa62","modified":1631633856066},{"_id":"public/categories/电磁场/index.html","hash":"02ba820d83001fe395004b9c39db5c947d123b67","modified":1631633856066},{"_id":"public/categories/网络通信/index.html","hash":"abafb1eace5d749d0d6fb907f726178201ccb74c","modified":1631633856066},{"_id":"public/categories/网络安全/index.html","hash":"ad7729956076715a209fb189782ec355fd5235b6","modified":1631633856066},{"_id":"public/categories/电磁场/矢量/index.html","hash":"f2a13ddb98d5f868c6c7469e26a7579958d5e6af","modified":1631633856066},{"_id":"public/index.html","hash":"3c7f2494f84dfa0fe16cd98d54ce6472b38e62a4","modified":1631633856066},{"_id":"public/page/2/index.html","hash":"d75c2b63057b80bae4d0174a62e01fff63dbc4b4","modified":1631633856066},{"_id":"public/page/3/index.html","hash":"419ebe03ae47eade03c63a99c43b12b235da77eb","modified":1631633856066},{"_id":"public/page/4/index.html","hash":"318efec312561a848d6fd2acb63d56637223cbb9","modified":1631633856066},{"_id":"public/page/5/index.html","hash":"11cf71182f0b050d05f07a4cf849bfa7985a5bc8","modified":1631633856066},{"_id":"public/page/6/index.html","hash":"2429e3eeac682aaeb4fea5b93c0d9b1fc26cad96","modified":1631633856066},{"_id":"public/page/8/index.html","hash":"0d27acda4c595eff4f073fb76363abd23766dd04","modified":1631633856066},{"_id":"public/page/7/index.html","hash":"cd5ad163189ab7ed849e35817bac94450eb72193","modified":1631633856066},{"_id":"public/page/9/index.html","hash":"3ef6fff0220a9aa0393b7211b82d9db61d6d9383","modified":1631633856066},{"_id":"public/page/10/index.html","hash":"c0bca5813654a92b10dc13bf0b975c820a41c593","modified":1631633856066},{"_id":"public/page/11/index.html","hash":"664f388f714426c7fc44542fd5f2acec530dba7a","modified":1631633856066},{"_id":"public/tags/51单片机/index.html","hash":"0548d7173c6b349dd77ae6ee429ac64b018d0905","modified":1631633856066},{"_id":"public/tags/Hexo/index.html","hash":"213a235becac8754f46baf7e8ff0c7c222e62847","modified":1631633856066},{"_id":"public/tags/Java面试/index.html","hash":"5f8055c1576891d5db2d2d217e27862143d22128","modified":1631633856066},{"_id":"public/tags/Maven/index.html","hash":"9d9adcbc7d74da9265116289163737f36876889e","modified":1631633856066},{"_id":"public/tags/MySQL/index.html","hash":"a177013670eb01837aa1a3de4dad74887b831997","modified":1631633856066},{"_id":"public/tags/spring/index.html","hash":"4dcbf62f17a585ef4a97884db24a0103d31978a8","modified":1631633856066},{"_id":"public/tags/spring/page/2/index.html","hash":"7affd12b7d8bc048a9060926fa909d8efc18e338","modified":1631633856066},{"_id":"public/tags/Mybatis/index.html","hash":"754a292da7ddc8e687974835ab90f8d4be187f74","modified":1631633856066},{"_id":"public/tags/esp8266/index.html","hash":"0264f427ca96d137607c2d682d4ff8778f324115","modified":1631633856066},{"_id":"public/tags/esp8266/page/2/index.html","hash":"629ca0e4fa7183e4af14370db512fd6afbdcb0bb","modified":1631633856066},{"_id":"public/tags/物联网/index.html","hash":"9309f73eb4c1552a0484eb8704b32420ccd4e842","modified":1631633856066},{"_id":"public/tags/springMVC/index.html","hash":"f531a754d87db249e79c5fd5f52b3e04db1c3135","modified":1631633856066},{"_id":"public/tags/Servlet/index.html","hash":"dfcb78a4427f5afb79d184ccfacdb55395aea2d5","modified":1631633856066},{"_id":"public/tags/Tomcat/index.html","hash":"a32b17c0384ec973148ebd31c0e40f841e8af7ea","modified":1631633856066},{"_id":"public/tags/javaSE/page/2/index.html","hash":"a33c49319f09534adb289ae8361f1612668d98e8","modified":1631633856066},{"_id":"public/tags/javaSE/page/3/index.html","hash":"deb72538264430394c65e91c0e5001e8a9939c88","modified":1631633856066},{"_id":"public/tags/javaSE/index.html","hash":"e0f9f158422ed596b1850cfea6bcaefce4d4c8fb","modified":1631633856066},{"_id":"public/tags/Http/index.html","hash":"a0143c82839fd1c5fd160b8e3bf14708e7f1bf28","modified":1631633856066},{"_id":"public/tags/win10/index.html","hash":"5c10df9f6c64f89352297e573b16277067e870af","modified":1631633856066},{"_id":"public/tags/MDK-keil5/index.html","hash":"9fc5a415ee288ed150092f6166684c6c7647a233","modified":1631633856066},{"_id":"public/tags/stm32/index.html","hash":"2a3cac3efadf3e057293efd9e4a5c0f2fabc399a","modified":1631633856066},{"_id":"public/tags/图床/index.html","hash":"038ae4bea02f30e45606f15a1fc3b91d8c955aaf","modified":1631633856066},{"_id":"public/tags/矢量/index.html","hash":"4dc07818b6f6ce8d5d971cd6802c6b3a5d2b26c7","modified":1631633856066},{"_id":"public/tags/网络安全/index.html","hash":"e4ce6e5fbbd10007db75288b9f3e3fb0e5fa599a","modified":1631633856066},{"_id":"public/tags/网络通信/index.html","hash":"e54eabdca13bb05f903b757364afaf24232e56b0","modified":1631633856066},{"_id":"public/img/404.jpg","hash":"fb4489bc1d30c93d28f7332158c1c6c1416148de","modified":1631633856066},{"_id":"public/img/algolia.svg","hash":"ec119560b382b2624e00144ae01c137186e91621","modified":1631633856066},{"_id":"public/img/articles.png","hash":"f6654630e0f6f18497475b34e607f63a79f09140","modified":1631633856066},{"_id":"public/img/articles2.png","hash":"44f2e90e928bf1ae3fd91a85b70b5b1d07a91519","modified":1631633856066},{"_id":"public/img/avatar1.jpg","hash":"466d1b7cd0573e57e7db294c5f63c55ee6ca4296","modified":1631633856066},{"_id":"public/img/avatar2.jpg","hash":"4a7143ede9561ace432c2ac963b406428b296756","modified":1631633856066},{"_id":"public/img/favicon.png","hash":"3cf89864b4f6c9b532522a4d260a2e887971c92d","modified":1631633856066},{"_id":"public/img/friend_404.gif","hash":"8d2d0ebef70a8eb07329f57e645889b0e420fa48","modified":1631633856066},{"_id":"public/img/huiji.png","hash":"f33b6927a0124ce7ad10638b96ee3be9b55c555e","modified":1631633856066},{"_id":"public/img/mybatis.png","hash":"1e685804796630baba037812e3beab62936e1227","modified":1631633856066},{"_id":"public/img/mybatis1.png","hash":"d40a9abf2799be2e830868845e7287e884d8688d","modified":1631633856066},{"_id":"public/img/pcbimg.png","hash":"72afcf1951d00c206d235facf6e09d16802c6ba1","modified":1631633856066},{"_id":"public/img/pcbimg2.png","hash":"511c4a15ad0f382adcbd3e1ff86dca3ead5d2df1","modified":1631633856066},{"_id":"public/img/plane.png","hash":"fcd0b34a638838dcae8cbf17f34c51f973a0c02b","modified":1631633856066},{"_id":"public/img/plane1.png","hash":"c5713f3d92ddcd45d2b9a494f78b0f5b0e4a7466","modified":1631633856066},{"_id":"public/live2d-widget/LICENSE","hash":"1eba7caf09a39110ad2f542e3ed8700d1a69c6d3","modified":1631633856066},{"_id":"public/mouse/AppStarting.ani","hash":"e334f5489e63167adbd44f1116ffcfc72e84f718","modified":1631633856066},{"_id":"public/mouse/Arrow.cur","hash":"1ab6471c728291cf66ba813b09135d48a06e74c3","modified":1631633856066},{"_id":"public/mouse/Cross.cur","hash":"6d6d6f975164d58db643a84ed238375fcbdac98d","modified":1631633856066},{"_id":"public/mouse/Hand.cur","hash":"46d6d4e0a8a79e054df35bcee1a53bb3c4b1ec8b","modified":1631633856066},{"_id":"public/mouse/Handwriting.cur","hash":"195c938abc7f3e6d1f2e6301c46ba1e42ff99a4f","modified":1631633856066},{"_id":"public/mouse/Help.cur","hash":"bf59506b7a65e2a5bb8c07b9b3c8daa6b90b0b7d","modified":1631633856066},{"_id":"public/mouse/IBeam.cur","hash":"c96a55e806892deb9c1866c1aa33f98627d6dadf","modified":1631633856066},{"_id":"public/mouse/SizeAll.cur","hash":"b8ccca4354a8c11d61b7281818004933bdc32e01","modified":1631633856066},{"_id":"public/mouse/NO.cur","hash":"709b03659cd3be203e9d1c449d675e866847b957","modified":1631633856066},{"_id":"public/mouse/SizeNWSE.cur","hash":"25666d52fb83373286dc0b815387be41a52a26ac","modified":1631633856066},{"_id":"public/mouse/SizeNESW.cur","hash":"253f4e4c0da9b85757596c09187a79204fd9f07b","modified":1631633856066},{"_id":"public/mouse/SizeWE.cur","hash":"915ae8029726e0cf7b8f523fff059a96148cfc5c","modified":1631633856066},{"_id":"public/mouse/UpArrow.cur","hash":"2e65c3437b457ddf6429a096d5921ea962703843","modified":1631633856066},{"_id":"public/mouse/SizeNS.cur","hash":"4c02aa53675e758f61de6e13a5cf764b41e129f5","modified":1631633856066},{"_id":"public/live2d-widget/assets/screenshot-1.png","hash":"4bd4d97a1fcaa5deb3cea1c0a102a895a15af32f","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearchLite.min.js","hash":"284416885e4e80e27fa4eae6fc305f4de15b914c","modified":1631633856066},{"_id":"public/fonts/JetBrainsMono-Medium.woff2","hash":"f31bdf436d68c69503e96e7ca691a90ffa4918a8","modified":1631633856066},{"_id":"public/mouse/Wait.ani","hash":"5a7278ded553b61a248ec549b9964b7e2930cecf","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearchLite.js","hash":"e56ad6b82caf69066de545201014291fc961635e","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearch.min.js","hash":"a3b131a9a47ccc16f4dd8988fabb6d306548db2f","modified":1631633856066},{"_id":"public/css/copyright.css","hash":"45bc0d0d4889daabae8b6e0c129617fcc212b77b","modified":1631633856066},{"_id":"public/css/background.css","hash":"573aeea20ddcc77fa2b835ebba66e40b90635fb3","modified":1631633856066},{"_id":"public/css/custom.css","hash":"43a20c653bba7892c704bf7654d60cfdcfe4b40d","modified":1631633856066},{"_id":"public/css/icon.css","hash":"d93c1e972e3569c9a08fedf3f54a1fe28f2b036f","modified":1631633856066},{"_id":"public/css/mouse.css","hash":"72d8fe42bc9b8896dabcbd163323841d6ee565a8","modified":1631633856066},{"_id":"public/css/side.css","hash":"770be1a4d7eed917770462cc8d361ead355985c5","modified":1631633856066},{"_id":"public/css/var.css","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1631633856066},{"_id":"public/js/utils.js","hash":"8319b59c26ce8cd2b0ae7d030c4912215148fa92","modified":1631633856066},{"_id":"public/live2d-widget/README.html","hash":"f4d5ae45aedcc8a1e6661dd7db64ca241c3758a5","modified":1631633856066},{"_id":"public/live2d-widget/autoload.js","hash":"e6540c988c9c2d6f57fabe4073469042fd4e088c","modified":1631633856066},{"_id":"public/live2d-widget/waifu-tips.js","hash":"f8bdc91fa1e51d46dc18e04174c63034d68299bf","modified":1631633856066},{"_id":"public/live2d-widget/waifu-tips.json","hash":"a3f9d4d832cd0948cd21385e7e8c62db377ba7d4","modified":1631633856066},{"_id":"public/live2d-widget/waifu.css","hash":"0613696628c20499c6f72bcec6b6780594443b0d","modified":1631633856066},{"_id":"public/js/search/algolia.js","hash":"65b45e61586f7e66c3f338370bfd9daadd71a4b7","modified":1631633856066},{"_id":"public/js/search/local-search.js","hash":"459e2541afda483916d16fce4aaa56b41bcd42ba","modified":1631633856066},{"_id":"public/live2d-widget/demo/login.html","hash":"9512df47f6516c0013153095693cba1992744f4b","modified":1631633856066},{"_id":"public/live2d-widget/demo/demo.html","hash":"2617b8aa4c4728d38a8f062465304e5433056abc","modified":1631633856066},{"_id":"public/css/index.css","hash":"09267c4348aec68b0104ec3ec2abb3fb5f1d067c","modified":1631633856066},{"_id":"public/live2d-widget/assets/screenshot-3.png","hash":"f89c6fdf44008e7d3c7a1aafe65dc39b212edb6c","modified":1631633856066},{"_id":"public/js/tw_cn.js","hash":"00053ce73210274b3679f42607edef1206eebc68","modified":1631633856066},{"_id":"public/js/main.js","hash":"8ef2821ceb92d81aa0f8c02ee932f094adcafb2b","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearch.js","hash":"6948fcdf071e4983e784e8c458cf201536f77792","modified":1631633856066},{"_id":"public/css/index.min.css","hash":"fbc05a25b9ffcb535debd43ba244c96e909498c1","modified":1631633856066},{"_id":"public/live2d-widget/assets/screenshot-2.png","hash":"76b844653136184c573b3632e12296e008a7ff5c","modified":1631633856066},{"_id":"public/img/02.jpg","hash":"52276fbc7427eaf3facebf1e7cf9d99dad1664e8","modified":1631633856066},{"_id":"public/img/banner.png","hash":"e0b6d5d65d72bdfeb1b1bcfd1af340088d50fdfb","modified":1631633856066},{"_id":"public/img/01.jpg","hash":"455aff642579a632713b90c58edcd89f1ea01aef","modified":1631633856066},{"_id":"public/img/34.jpg","hash":"10b2ddfbe7f03c4513ace7a2855a47f907478d55","modified":1631633856066},{"_id":"public/img/blissed01.jpg","hash":"932db4b0cb8a297a4f9c1fd48af59183db742584","modified":1631633856066},{"_id":"public/live2d-widget/live2d.min.js","hash":"94383fb61de815c3f806ae3f11026bbcfe384082","modified":1631633856066},{"_id":"public/img/categories.png","hash":"1829d3667c9fb36d729057a2fb26f12b662c86e4","modified":1631633856066},{"_id":"public/fonts/loving.ttf","hash":"786ab7558a8a2cd5de1ac71a1c6c19bfeff17e95","modified":1631633856066},{"_id":"public/fonts/shishangblack.ttf","hash":"b5dda0beb2618829e41553d0dc656afbc78af523","modified":1631633856066},{"_id":"public/fonts/ajLangMan.ttf","hash":"c79e63ca78049f8273c0de7b5c65898c1dd8557f","modified":1631633856066},{"_id":"public/fonts/ZhuZiAWan.woff2","hash":"a8512c6d5d70f5fc098a58a01a0ad720aa211c2a","modified":1631633856066}],"Category":[{"name":"硬件学习","_id":"cktk8o6o10004akve0nho022p"},{"name":"51单片机","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6oe000sakvef4qjae8o"},{"name":"Hexo","_id":"cktk8o6ov001sakve83h63fm7"},{"name":"java","_id":"cktk8o6pc002lakve2iyjem8n"},{"name":"spring","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qe0055akvegob69vva"},{"name":"java面试","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qh005bakveacjzeuio"},{"name":"Maven","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qs0068akve66aj2w3e"},{"name":"Mybatis","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r30076akve2siqh8cu"},{"name":"MySQL","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r5007eakve6g5i7k0f"},{"name":"esp8266","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rl008wakveg62hemot"},{"name":"物联网开发","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rz00a5akvehafeesla"},{"name":"springMVC","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6s900beakvegzr10ttv"},{"name":"javaEE","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sj00cnakvedfi1cpk5"},{"name":"javaSE","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sq00dhakve6e73glbu"},{"name":"win10","_id":"cktk8o6td00fzakve55x24m4x"},{"name":"stm32","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6te00g7akvedfi64j0n"},{"name":"杂七杂八","_id":"cktk8o6th00gkakve2e1eb0kc"},{"name":"电磁场","_id":"cktk8o6th00goakve1pju20c4"},{"name":"网络通信","_id":"cktk8o6th00grakve7iws2msc"},{"name":"网络安全","_id":"cktk8o6ti00gtakve51ks4u05"},{"name":"矢量","parent":"cktk8o6th00goakve1pju20c4","_id":"cktk8o6ti00gwakve0x2ufj97"}],"Data":[{"_id":"link","data":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}],"Page":[{"_content":"{\n \"name\": \"string\",\n \"short_name\": \"Junzhou\",\n \"theme_color\": \"#49b1f5\",\n \"background_color\": \"#49b1f5\",\n \"display\": \"standalone\",\n \"scope\": \"/\",\n \"start_url\": \"/\",\n \"icons\": [\n {\n \"src\": \"images/pwaicons/36.png\",\n \"sizes\": \"36x36\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/48.png\",\n \"sizes\": \"48x48\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/72.png\",\n \"sizes\": \"72x72\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/96.png\",\n \"sizes\": \"96x96\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/144.png\",\n \"sizes\": \"144x144\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/192.png\",\n \"sizes\": \"192x192\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/512.png\",\n \"sizes\": \"512x512\",\n \"type\": \"image/png\"\n }\n ],\n \"splash_pages\": null\n }","source":"manifest.json","raw":"{\n \"name\": \"string\",\n \"short_name\": \"Junzhou\",\n \"theme_color\": \"#49b1f5\",\n \"background_color\": \"#49b1f5\",\n \"display\": \"standalone\",\n \"scope\": \"/\",\n \"start_url\": \"/\",\n \"icons\": [\n {\n \"src\": \"images/pwaicons/36.png\",\n \"sizes\": \"36x36\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/48.png\",\n \"sizes\": \"48x48\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/72.png\",\n \"sizes\": \"72x72\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/96.png\",\n \"sizes\": \"96x96\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/144.png\",\n \"sizes\": \"144x144\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/192.png\",\n \"sizes\": \"192x192\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/512.png\",\n \"sizes\": \"512x512\",\n \"type\": \"image/png\"\n }\n ],\n \"splash_pages\": null\n }","date":"2021-09-14T09:24:37.372Z","updated":"2021-09-14T09:24:37.371Z","path":"manifest.json","layout":"false","title":"","comments":1,"_id":"cktk8o6nt0000akvebb85cs9q","content":"{\"name\":\"string\",\"short_name\":\"Junzhou\",\"theme_color\":\"#49b1f5\",\"background_color\":\"#49b1f5\",\"display\":\"standalone\",\"scope\":\"/\",\"start_url\":\"/\",\"icons\":[{\"src\":\"images/pwaicons/36.png\",\"sizes\":\"36x36\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/48.png\",\"sizes\":\"48x48\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/72.png\",\"sizes\":\"72x72\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/96.png\",\"sizes\":\"96x96\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/144.png\",\"sizes\":\"144x144\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/512.png\",\"sizes\":\"512x512\",\"type\":\"image/png\"}],\"splash_pages\":null}","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"{\"name\":\"string\",\"short_name\":\"Junzhou\",\"theme_color\":\"#49b1f5\",\"background_color\":\"#49b1f5\",\"display\":\"standalone\",\"scope\":\"/\",\"start_url\":\"/\",\"icons\":[{\"src\":\"images/pwaicons/36.png\",\"sizes\":\"36x36\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/48.png\",\"sizes\":\"48x48\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/72.png\",\"sizes\":\"72x72\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/96.png\",\"sizes\":\"96x96\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/144.png\",\"sizes\":\"144x144\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/512.png\",\"sizes\":\"512x512\",\"type\":\"image/png\"}],\"splash_pages\":null}"},{"title":"关于","date":"2021-08-20T14:32:50.000Z","type":"about","_content":"\n2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。\n\n\n\n嗯。。。。。。。\n就这样!!!!!","source":"about/index.md","raw":"---\ntitle: 关于\ndate: 2021-08-20 22:32:50\ntype: \"about\"\n---\n\n2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。\n\n\n\n嗯。。。。。。。\n就这样!!!!!","updated":"2021-08-20T14:37:08.214Z","path":"about/index.html","comments":1,"layout":"page","_id":"cktk8o6ny0002akve6dacfapw","content":"

2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。

\n

嗯。。。。。。。
就这样!!!!!

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"

2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。

\n

嗯。。。。。。。
就这样!!!!!

\n"},{"title":"友情链接","date":"2021-08-17T05:12:28.000Z","type":"link","_content":"","source":"link/index.md","raw":"---\ntitle: 友情链接\ndate: 2021-08-17 13:12:28\ntype: link\n---\n","updated":"2021-08-17T05:12:52.461Z","path":"link/index.html","comments":1,"layout":"page","_id":"cktk8o6o20006akve6elihxpj","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":""},{"title":"音乐","date":"2021-08-17T13:17:27.000Z","type":"music","_content":"\n\n{% tabs 音乐, -1 %}\n\n[网易云音乐-我的音乐](https://music.163.com/#/my/m/music/playlist?id=5322139721)\n\n{% folding 歌单1 %}\n{% meting \"6925236447\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-09 %}\n{% meting \"6952136420\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-08 %}\n{% meting \"6916454908\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-06 %}\n{% meting \"6826165381\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-05 %}\n{% meting \"6749189366\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-04[2] %}\n{% meting \"6718667049\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-04[1] %}\n{% meting \"6678848086\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-03%}\n{% meting \"6649685822\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷狗音乐](https://www.kugou.com/)\n\n{% folding 大杂烩%}\n{% meting \"37118256\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"38928927\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"12869806\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[QQ音乐](https://y.qq.com/)\n\nQQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是\n\n{% folding 歌单1%}\n{% meting \"8133710813\" \"tencent\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷我音乐](http://www.kuwo.cn/)\n\n{% folding 展开%}\n\n\n\n{% endfolding %}\n\n\n\n\n\n\n芳华慢-封茗囧菌\n{% audio https://webfs.ali.kugou.com/202109120929/fdccfb42189820d5c6cac2e9ec9f7e8b/KGTX/CLTX001/4e38a814fe40ae680ab67ced203487a5.mp3 %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n","source":"music/index.md","raw":"---\ntitle: 音乐\ndate: 2021-08-17 21:17:27\ntype: \"music\"\n---\n\n\n{% tabs 音乐, -1 %}\n\n[网易云音乐-我的音乐](https://music.163.com/#/my/m/music/playlist?id=5322139721)\n\n{% folding 歌单1 %}\n{% meting \"6925236447\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-09 %}\n{% meting \"6952136420\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-08 %}\n{% meting \"6916454908\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-06 %}\n{% meting \"6826165381\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-05 %}\n{% meting \"6749189366\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-04[2] %}\n{% meting \"6718667049\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-04[1] %}\n{% meting \"6678848086\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-03%}\n{% meting \"6649685822\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷狗音乐](https://www.kugou.com/)\n\n{% folding 大杂烩%}\n{% meting \"37118256\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"38928927\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"12869806\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[QQ音乐](https://y.qq.com/)\n\nQQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是\n\n{% folding 歌单1%}\n{% meting \"8133710813\" \"tencent\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷我音乐](http://www.kuwo.cn/)\n\n{% folding 展开%}\n\n\n\n{% endfolding %}\n\n\n\n\n\n\n芳华慢-封茗囧菌\n{% audio https://webfs.ali.kugou.com/202109120929/fdccfb42189820d5c6cac2e9ec9f7e8b/KGTX/CLTX001/4e38a814fe40ae680ab67ced203487a5.mp3 %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n","updated":"2021-09-12T08:52:58.221Z","path":"music/index.html","comments":1,"layout":"page","_id":"cktk8o6o40008akve8p3pgh18","content":"

网易云音乐-我的音乐

\n
歌单1 \n
\n
\n
\n
\n\n
歌单2021-09 \n
\n
\n
\n
\n\n
歌单2021-08 \n
\n
\n
\n
\n\n\n
歌单2021-06 \n
\n
\n
\n
\n\n\n\n
歌单2021-05 \n
\n
\n
\n
\n\n\n\n
歌单2021-04[2] \n
\n
\n
\n
\n\n\n
歌单2021-04[1] \n
\n
\n
\n
\n\n\n\n
歌单2021-03 \n
\n
\n
\n

酷狗音乐

\n
大杂烩 \n
\n
\n
\n

QQ音乐

\n

QQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是

\n
歌单1 \n
\n
\n
\n

酷我音乐
<!—
<div id=”aplayer-jyZgFIvs” class=”aplayer aplayer-tag-marker meting-tag-marker”
data-id=”72510362” data-server=”kuwo” data-type=”yinyue” data-mode=”circulation” data-autoplay=”false” data-mutex=”true” data-listmaxheight=”340px” data-preload=”auto” data-theme=”#3F51B5”

\n
></div> -->\n
展开 \n
\n \n
\n

芳华慢-封茗囧菌

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

网易云音乐-我的音乐

\n
歌单1 \n
\n
\n
\n
\n\n
歌单2021-09 \n
\n
\n
\n
\n\n
歌单2021-08 \n
\n
\n
\n
\n\n\n
歌单2021-06 \n
\n
\n
\n
\n\n\n\n
歌单2021-05 \n
\n
\n
\n
\n\n\n\n
歌单2021-04[2] \n
\n
\n
\n
\n\n\n
歌单2021-04[1] \n
\n
\n
\n
\n\n\n\n
歌单2021-03 \n
\n
\n
\n

酷狗音乐

\n
大杂烩 \n
\n
\n
\n

QQ音乐

\n

QQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是

\n
歌单1 \n
\n
\n
\n

酷我音乐
<!—
<div id=”aplayer-jyZgFIvs” class=”aplayer aplayer-tag-marker meting-tag-marker”
data-id=”72510362” data-server=”kuwo” data-type=”yinyue” data-mode=”circulation” data-autoplay=”false” data-mutex=”true” data-listmaxheight=”340px” data-preload=”auto” data-theme=”#3F51B5”

\n
></div> -->\n
展开 \n
\n \n
\n

芳华慢-封茗囧菌

\n
\n"},{"title":"分类","date":"2021-08-17T05:10:35.000Z","type":"categories","_content":"","source":"categories/index.md","raw":"---\ntitle: 分类\ndate: 2021-08-17 13:10:35\ntype: \"categories\"\n# top_img: false\n---\n","updated":"2021-08-17T11:26:53.838Z","path":"categories/index.html","comments":1,"layout":"page","_id":"cktk8o6o5000aakve8jlidwd2","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":""},{"title":"笔记","date":"2021-09-01T00:27:26.000Z","type":"notes","_content":"","source":"notes/index.md","raw":"---\ntitle: 笔记\ndate: 2021-09-01 08:27:26\ntype: \"notes\"\n---\n","updated":"2021-09-01T00:28:37.545Z","path":"notes/index.html","comments":1,"layout":"page","_id":"cktk8o6o9000fakveeaeogkyt","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":""},{"title":"标签","date":"2021-08-17T05:11:25.000Z","type":"tags","_content":"","source":"tags/index.md","raw":"---\ntitle: 标签\ndate: 2021-08-17 13:11:25\ntype: \"tags\"\n---\n","updated":"2021-08-17T05:11:46.636Z","path":"tags/index.html","comments":1,"layout":"page","_id":"cktk8o6oa000iakve6bb9guiq","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":""},{"title":"诗歌","date":"2021-09-01T05:19:34.000Z","type":"poems","_content":"\n{% folding blue, 水调歌头 %}\n{% poem 水调歌头,苏轼 %}\n丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。\n明月几时有?把酒问青天。\n不知天上宫阙,今夕是何年?\n我欲乘风归去,又恐琼楼玉宇,高处不胜寒。\n起舞弄清影,何似在人间?\n\n转朱阁,低绮户,照无眠。\n不应有恨,何事长向别时圆?\n人有悲欢离合,月有阴晴圆缺,此事古难全。\n但愿人长久,千里共婵娟。\n{% endpoem %}\n{%endfolding%}\n\n{% folding blue, 古诗十九首 %}\n{% poem 迢迢牵牛星 %}\n迢迢牵牛星,皎皎河汉女。\n纤纤擢素手,札札弄机杼。\n终日不成章,泣涕零如雨。\n河汉清且浅,相去复几许?\n盈盈一水间,脉脉不得语。\n{% endpoem %}\n\n{% poem 行行重行行 %}\n行行重行行,与君生别离。\n相去万余里,各在天一涯。\n道路阻且长,会面安可知?\n胡马依北风,越鸟巢南枝。\n相去日已远,衣带日已缓。\n浮云蔽白日,游子不顾反。\n思君令人老,岁月忽已晚。\n弃捐勿复道,努力加餐饭\n{% endpoem %}\n\n\n{%endfolding%}\n\n","source":"poems/index.md","raw":"---\ntitle: 诗歌\ndate: 2021-09-01 13:19:34\ntype: \"poems\"\n---\n\n{% folding blue, 水调歌头 %}\n{% poem 水调歌头,苏轼 %}\n丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。\n明月几时有?把酒问青天。\n不知天上宫阙,今夕是何年?\n我欲乘风归去,又恐琼楼玉宇,高处不胜寒。\n起舞弄清影,何似在人间?\n\n转朱阁,低绮户,照无眠。\n不应有恨,何事长向别时圆?\n人有悲欢离合,月有阴晴圆缺,此事古难全。\n但愿人长久,千里共婵娟。\n{% endpoem %}\n{%endfolding%}\n\n{% folding blue, 古诗十九首 %}\n{% poem 迢迢牵牛星 %}\n迢迢牵牛星,皎皎河汉女。\n纤纤擢素手,札札弄机杼。\n终日不成章,泣涕零如雨。\n河汉清且浅,相去复几许?\n盈盈一水间,脉脉不得语。\n{% endpoem %}\n\n{% poem 行行重行行 %}\n行行重行行,与君生别离。\n相去万余里,各在天一涯。\n道路阻且长,会面安可知?\n胡马依北风,越鸟巢南枝。\n相去日已远,衣带日已缓。\n浮云蔽白日,游子不顾反。\n思君令人老,岁月忽已晚。\n弃捐勿复道,努力加餐饭\n{% endpoem %}\n\n\n{%endfolding%}\n\n","updated":"2021-09-01T11:21:57.769Z","path":"poems/index.html","comments":1,"layout":"page","_id":"cktk8o6oc000nakve8s3d8v6m","content":"
水调歌头 \n
\n
水调歌头
苏轼

丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。
明月几时有?把酒问青天。
不知天上宫阙,今夕是何年?
我欲乘风归去,又恐琼楼玉宇,高处不胜寒。
起舞弄清影,何似在人间?

转朱阁,低绮户,照无眠。
不应有恨,何事长向别时圆?
人有悲欢离合,月有阴晴圆缺,此事古难全。
但愿人长久,千里共婵娟。

\n
\n
\n
古诗十九首 \n
\n
迢迢牵牛星

迢迢牵牛星,皎皎河汉女。
纤纤擢素手,札札弄机杼。
终日不成章,泣涕零如雨。
河汉清且浅,相去复几许?
盈盈一水间,脉脉不得语。

行行重行行

行行重行行,与君生别离。
相去万余里,各在天一涯。
道路阻且长,会面安可知?
胡马依北风,越鸟巢南枝。
相去日已远,衣带日已缓。
浮云蔽白日,游子不顾反。
思君令人老,岁月忽已晚。
弃捐勿复道,努力加餐饭

\n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"
水调歌头 \n
\n
水调歌头
苏轼

丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。
明月几时有?把酒问青天。
不知天上宫阙,今夕是何年?
我欲乘风归去,又恐琼楼玉宇,高处不胜寒。
起舞弄清影,何似在人间?

转朱阁,低绮户,照无眠。
不应有恨,何事长向别时圆?
人有悲欢离合,月有阴晴圆缺,此事古难全。
但愿人长久,千里共婵娟。

\n
\n
\n
古诗十九首 \n
\n
迢迢牵牛星

迢迢牵牛星,皎皎河汉女。
纤纤擢素手,札札弄机杼。
终日不成章,泣涕零如雨。
河汉清且浅,相去复几许?
盈盈一水间,脉脉不得语。

行行重行行

行行重行行,与君生别离。
相去万余里,各在天一涯。
道路阻且长,会面安可知?
胡马依北风,越鸟巢南枝。
相去日已远,衣带日已缓。
浮云蔽白日,游子不顾反。
思君令人老,岁月忽已晚。
弃捐勿复道,努力加餐饭

\n
\n
\n"},{"title":"网站链接","date":"2021-09-01T01:38:54.000Z","type":"web_links","_content":"\n\n{% folding blue, 工具网站 %}\n\n\n\n\n1. [百度脑图-便捷的思维工具](https://naotu.baidu.com/home)\n2. [【GitMind官网】- 免费在线思维导图软件](https://gitmind.cn/)\n3. [程序员工具 - 聚够网](http://tool.ggo.net/)\n4. [程序员常用工具箱 -TOOLFK](https://www.toolfk.com/)\n5. [json解析](http://sojson.com/)\n6. [微词云 - 制作 - 设计页](https://www.weiciyun.com/u/create#/?k=kecplc6u)\n7. [在线生成透明ICO图标——ICO图标制作](http://www.ico51.cn/)\n8. [math-latex](https://webdemo.myscript.com/views/math/index.html#)\n9. [LaTeX在线:吴文中数学公式编辑器](https://latex.91maths.com/)\n10. [638+ 演示文稿模板 - 模板素材库 - Canva可画](https://www.canva.cn/templates/search/5ryU56S65paH56i_X64/)\n11. [ppt模板 - 学堂PPT](https://xtppt.cn/PPTMB/YQPPT/1792.html)\n12. [JetBrains](https://www.jetbrains.com/)\n13. [GitHub 下载文件加速](https://gh.api.99988866.xyz/)\n14. [博客文章图片生成](https://nav.rdonly.com/laboratory/bgimage/backimage.html)\n15. [徽标生成工具](https://shields.io/)\n\n\n{% endfolding %}","source":"web-links/index.md","raw":"---\ntitle: 网站链接\ndate: 2021-09-01 09:38:54\ntype: \"web_links\"\n---\n\n\n{% folding blue, 工具网站 %}\n\n\n\n\n1. [百度脑图-便捷的思维工具](https://naotu.baidu.com/home)\n2. [【GitMind官网】- 免费在线思维导图软件](https://gitmind.cn/)\n3. [程序员工具 - 聚够网](http://tool.ggo.net/)\n4. [程序员常用工具箱 -TOOLFK](https://www.toolfk.com/)\n5. [json解析](http://sojson.com/)\n6. [微词云 - 制作 - 设计页](https://www.weiciyun.com/u/create#/?k=kecplc6u)\n7. [在线生成透明ICO图标——ICO图标制作](http://www.ico51.cn/)\n8. [math-latex](https://webdemo.myscript.com/views/math/index.html#)\n9. [LaTeX在线:吴文中数学公式编辑器](https://latex.91maths.com/)\n10. [638+ 演示文稿模板 - 模板素材库 - Canva可画](https://www.canva.cn/templates/search/5ryU56S65paH56i_X64/)\n11. [ppt模板 - 学堂PPT](https://xtppt.cn/PPTMB/YQPPT/1792.html)\n12. [JetBrains](https://www.jetbrains.com/)\n13. [GitHub 下载文件加速](https://gh.api.99988866.xyz/)\n14. [博客文章图片生成](https://nav.rdonly.com/laboratory/bgimage/backimage.html)\n15. [徽标生成工具](https://shields.io/)\n\n\n{% endfolding %}","updated":"2021-09-01T02:05:12.181Z","path":"web-links/index.html","comments":1,"layout":"page","_id":"cktk8o6od000qakveak0t3zck","content":"
工具网站 \n
\n
  1. 百度脑图-便捷的思维工具
  2. 【GitMind官网】- 免费在线思维导图软件
  3. 程序员工具 - 聚够网
  4. 程序员常用工具箱 -TOOLFK
  5. json解析
  6. 微词云 - 制作 - 设计页
  7. 在线生成透明ICO图标——ICO图标制作
  8. math-latex
  9. LaTeX在线:吴文中数学公式编辑器
  10. 638+ 演示文稿模板 - 模板素材库 - Canva可画
  11. ppt模板 - 学堂PPT
  12. JetBrains
  13. GitHub 下载文件加速
  14. 博客文章图片生成
  15. 徽标生成工具
\n
\n
","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"
工具网站 \n
\n
  1. 百度脑图-便捷的思维工具
  2. 【GitMind官网】- 免费在线思维导图软件
  3. 程序员工具 - 聚够网
  4. 程序员常用工具箱 -TOOLFK
  5. json解析
  6. 微词云 - 制作 - 设计页
  7. 在线生成透明ICO图标——ICO图标制作
  8. math-latex
  9. LaTeX在线:吴文中数学公式编辑器
  10. 638+ 演示文稿模板 - 模板素材库 - Canva可画
  11. ppt模板 - 学堂PPT
  12. JetBrains
  13. GitHub 下载文件加速
  14. 博客文章图片生成
  15. 徽标生成工具
\n
\n
"}],"Post":[{"title":"51单片机学习笔记(一)","description":"点亮第一个led灯、流水灯、蜂鸣器、数码管、按键","abbrlink":"9e0a8624","date":"2021-08-17T16:00:00.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n## 点亮第一个led灯\n\n\n\n1. 新建工程\n\n\t![image-20210821203338653](https://gitee.com/ajream/images/raw/master/img/20210821203342_image-20210821203338653.png)\n\n2. 选择固件\n\n\t![image-20210821203543242](https://gitee.com/ajream/images/raw/master/img/20210821203544_image-20210821203543242.png)\n\n\t![image-20210821203717166](https://gitee.com/ajream/images/raw/master/img/20210821203718_image-20210821203717166.png)\n\n3. 新建代码源文件\n\n\t![image-20210821203932209](https://gitee.com/ajream/images/raw/master/img/20210821203933_image-20210821203932209.png)\n\n\t![image-20210821204130367](https://gitee.com/ajream/images/raw/master/img/20210821204131_image-20210821204130367.png)\n\n\t![image-20210821204227781](https://gitee.com/ajream/images/raw/master/img/20210821204229_image-20210821204227781.png)\n\n4. 添加如下代码\n\n\t```c\n\t#include //引用51头文件\n\t\n\tsbit LED = P1^1;\n\t\n\tvoid main()\n\t{\n\t\tLED = 0;\t//点亮LED2\t\n\t}\n\t```\n5. 编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面\n\n ![image-20210821204723747](https://gitee.com/ajream/images/raw/master/img/20210821204724_image-20210821204723747.png)\n6. 编译\n\n ![image-20210821205109242](https://gitee.com/ajream/images/raw/master/img/20210821205110_image-20210821205109242.png)\n\n7. 上传(烧录)\n\n ![image-20210821205434569](https://gitee.com/ajream/images/raw/master/img/20210821205435_image-20210821205434569.png)\n\n\n\n到此第一个点灯小程序结束\n\n\n\n\n\n## 流水灯\n\n\n\n按照之前的步骤再建立一个新工程,写下如下代码:\n\n```c\n#include \t //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nuchar temp; //LED灯相关变量\n\n//延时函数\nvoid delay(uint z)\n{\n\tuint x, y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\ttemp = 0xfe;\n\tP1 = temp; //1111 1110 初值LED1亮\n\tdelay(1000);//毫秒级延时 100毫秒\n\twhile(1)\n\t{\n\t\ttemp = _cror_(temp, 1);//循环左移\n\t\tP1 = temp;//移位完成后赋值给P1 每个一个灯点亮\n\t\tdelay(1000);//毫秒级延时 100毫秒\n\t}\t\n} \n```\n\n说明:\n\n- `intrins.h` 头文件包含移位函数 :`右移_cror_()`、 `左移_corl_()`\n\n\n\n## 蜂鸣器\n\n蜂鸣器分有源与无源蜂鸣器\n\n- 有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音\n- 无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响\n\n一个蜂鸣器的简单驱动电路:\n\n![image-20210821210804064](https://gitee.com/ajream/images/raw/master/img/20210821210805_image-20210821210804064.png)\n\n```c\n#include \t//包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n\nsbit beep = P2^3;//蜂鸣器\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\twhile(1)\n\t{\n\t\tbeep = ~beep;//\t蜂鸣器发出滴滴声\n\t\tdelay(100);\n\t}\t\n} \n```\n\n\n\n\n\n## 数码管\n\n\n\n### 原理\n\n单个数码管结构原理:\n\n![image-20210821211542277](https://gitee.com/ajream/images/raw/master/img/20210821211543_image-20210821211542277.png)\n\n\n\n多个数码管:\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n说明:\n\n74HC573是个锁存器,原理如下:\n\n![image-20210821212211630](https://gitee.com/ajream/images/raw/master/img/20210821212212_image-20210821212211630.png)\n\n当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化\n\n当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态\n\n\n\n在这幅图\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n\n\n用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符\n\n在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即`1111 1110`),最左边这个数码管就被选中了。\n\n数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下\n\n```c\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F\n// 0 1 2 3\t4\t 5\t 6 7 8 9 \n0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x40, 0x00\n//A B C D E F H L - 熄灭\n```\n\n\n\n### 数码管静态显示\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管位选\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110 选通最左边的数码管\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\tP0 = 0X5b;//1101 1011 显示“1”\n\tDU = 0;//锁存段选数据\n\t\n\twhile(1);\n} \n```\n\n\n\n### 数码管动态显示\n\n要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n*8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即**快速扫描**每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示\n\n\n\n这个代码用来控制3个数码管来显示一个三位数\n\n```c\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n//设计一个函数用于显示三位的数字number\nvoid display(uchar number)\n{\n\tuchar bai, shi, ge;\n\tbai = number / 100; \t\t//百位\n\tshi = number % 100 / 10;\t//十位\n\tge = number % 10;\t\t\t//个位\n\t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\nvoid main()//main函数自身会循环\n{\t\n\twhile(1)\n\t{\n\t\tdisplay(185); //数码管显示185\n\t}\t\n} \n```\n\n\n\n\n\n\n\n## 键盘\n\n非编码键盘分为独立键盘和矩阵键盘\n\n### 独立键盘\n\n\n\n![image-20210821222715524](https://gitee.com/ajream/images/raw/master/img/20210821222718_image-20210821222715524.png)\n\n对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测\n\nP30是否为低电平即可\n\n> 【注意】\n>\n> 1. 按键消抖,最简单的是延时10~20ms后再检查其状态是否变化\n> 2. 松手检测\n\n```c\n/*********************************************************************************\n \t按下开发板S2按键数码管值+1,最大到9\n\t按下S3按下,值-1,最小减到0\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\n\n//毫秒级延时函数定义\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 9)//如果值不等于9则+1,功能把值限定为小于9\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\t//如果大于0则执行减一\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t\t//松手之后刷新显示\n\t\tDU = 1;//打开段选锁存器\n\t\tP0 = tabel[num];//\n\t\tDU = 0;//锁存段选数据\n\t}\t\n} \n```\n\n\n\n### 矩阵键盘\n\n判断矩阵按键中某个按键是否按下可采用**先列扫描再行扫描**的方式\n\n1. 列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了;\n2. 行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。\n\n\n\n```c\n/*********************************************************************************\n* 【程序功能】: \t4*4矩阵键盘与4位独立键盘识别\t\t \t\t\t \t\t\t \n* 【使用说明】: \t按下矩阵键盘和独立键盘任意键,数码管显示相应数值\n\t\t\t\t 初始显示“-”横\n**********************************************************************************/\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\nuchar KeyValue = 20;//按键值 显示-\n\n//共阴数码管段选表\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x37, 0x3E, 0x40, 0x00 }; \n//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,H,L,,n,u,-,熄灭\n\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid KeyScan()\n{\n\t//4*4矩阵键盘扫描\n\tP3 = 0XF0;//列扫描\n\tif(P3 != 0XF0)//判断按键是否被按下\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XF0)//判断按键是否被按下\n\t\t{\n\t\t\tswitch(P3) //判断那一列被按下\n\t\t\t{\n\t\t\t\tcase 0xe0:\tKeyValue = 0;\tbreak;//第一列被按下\n\t\t\t\tcase 0xd0:\tKeyValue = 1;\tbreak;//第二列被按下\n\t\t\t\tcase 0xb0:\tKeyValue = 2;\tbreak;//第三列被按下\n\t\t\t\tcase 0x70:\tKeyValue = 3;\tbreak;//第四列被按下 \n\t\t\t}\n\t\t\tP3 = 0X0F;//行扫描\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0x0e:\tKeyValue = KeyValue;\tbreak;//第一行被按下\n\t\t\t\tcase 0x0d:\tKeyValue = KeyValue + 4;\tbreak;//第二行被按下\n\t\t\t\tcase 0x0b:\tKeyValue = KeyValue + 8;\tbreak;//第三行被按下\n\t\t\t\tcase 0x07:\tKeyValue = KeyValue + 12;\tbreak;//第四行被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0X0F);//松手检测\t\n\t\t}\n\t}\n \n\tP3 = 0XFF;//独立按键扫描\n\tif(P3 != 0XFF)\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XFF)\n\t\t{\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0xfe:\tKeyValue = 16;\tbreak;//S2被按下\n\t\t\t\tcase 0xfd:\tKeyValue = 17;\tbreak;//S3被按下\n\t\t\t\tcase 0xfb:\tKeyValue = 18;\tbreak;//S4被按下\n\t\t\t\tcase 0xf7:\tKeyValue = 19;\tbreak;//S5被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0XFF);//松手检测\t\t\t\n\t\t}\t\n\t}\n\n}\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\twhile(1)\n\t{\n\t\tKeyScan();//20个按键键盘扫描\n\t\tP0 = tabel[KeyValue];//显示按键值\n\t}\t\n} \n```\n\n\n\n> 总结:先列扫描,再行扫描;或者先行扫描,再列扫描\n\n\n\n\n\n","source":"_posts/51单片机/51单片机学习笔记(一).md","raw":"---\ntitle: 51单片机学习笔记(一)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 点亮第一个led灯、流水灯、蜂鸣器、数码管、按键\nabbrlink: 9e0a8624\ndate: 2021-08-18 00:00:00\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n## 点亮第一个led灯\n\n\n\n1. 新建工程\n\n\t![image-20210821203338653](https://gitee.com/ajream/images/raw/master/img/20210821203342_image-20210821203338653.png)\n\n2. 选择固件\n\n\t![image-20210821203543242](https://gitee.com/ajream/images/raw/master/img/20210821203544_image-20210821203543242.png)\n\n\t![image-20210821203717166](https://gitee.com/ajream/images/raw/master/img/20210821203718_image-20210821203717166.png)\n\n3. 新建代码源文件\n\n\t![image-20210821203932209](https://gitee.com/ajream/images/raw/master/img/20210821203933_image-20210821203932209.png)\n\n\t![image-20210821204130367](https://gitee.com/ajream/images/raw/master/img/20210821204131_image-20210821204130367.png)\n\n\t![image-20210821204227781](https://gitee.com/ajream/images/raw/master/img/20210821204229_image-20210821204227781.png)\n\n4. 添加如下代码\n\n\t```c\n\t#include //引用51头文件\n\t\n\tsbit LED = P1^1;\n\t\n\tvoid main()\n\t{\n\t\tLED = 0;\t//点亮LED2\t\n\t}\n\t```\n5. 编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面\n\n ![image-20210821204723747](https://gitee.com/ajream/images/raw/master/img/20210821204724_image-20210821204723747.png)\n6. 编译\n\n ![image-20210821205109242](https://gitee.com/ajream/images/raw/master/img/20210821205110_image-20210821205109242.png)\n\n7. 上传(烧录)\n\n ![image-20210821205434569](https://gitee.com/ajream/images/raw/master/img/20210821205435_image-20210821205434569.png)\n\n\n\n到此第一个点灯小程序结束\n\n\n\n\n\n## 流水灯\n\n\n\n按照之前的步骤再建立一个新工程,写下如下代码:\n\n```c\n#include \t //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nuchar temp; //LED灯相关变量\n\n//延时函数\nvoid delay(uint z)\n{\n\tuint x, y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\ttemp = 0xfe;\n\tP1 = temp; //1111 1110 初值LED1亮\n\tdelay(1000);//毫秒级延时 100毫秒\n\twhile(1)\n\t{\n\t\ttemp = _cror_(temp, 1);//循环左移\n\t\tP1 = temp;//移位完成后赋值给P1 每个一个灯点亮\n\t\tdelay(1000);//毫秒级延时 100毫秒\n\t}\t\n} \n```\n\n说明:\n\n- `intrins.h` 头文件包含移位函数 :`右移_cror_()`、 `左移_corl_()`\n\n\n\n## 蜂鸣器\n\n蜂鸣器分有源与无源蜂鸣器\n\n- 有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音\n- 无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响\n\n一个蜂鸣器的简单驱动电路:\n\n![image-20210821210804064](https://gitee.com/ajream/images/raw/master/img/20210821210805_image-20210821210804064.png)\n\n```c\n#include \t//包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n\nsbit beep = P2^3;//蜂鸣器\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\twhile(1)\n\t{\n\t\tbeep = ~beep;//\t蜂鸣器发出滴滴声\n\t\tdelay(100);\n\t}\t\n} \n```\n\n\n\n\n\n## 数码管\n\n\n\n### 原理\n\n单个数码管结构原理:\n\n![image-20210821211542277](https://gitee.com/ajream/images/raw/master/img/20210821211543_image-20210821211542277.png)\n\n\n\n多个数码管:\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n说明:\n\n74HC573是个锁存器,原理如下:\n\n![image-20210821212211630](https://gitee.com/ajream/images/raw/master/img/20210821212212_image-20210821212211630.png)\n\n当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化\n\n当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态\n\n\n\n在这幅图\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n\n\n用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符\n\n在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即`1111 1110`),最左边这个数码管就被选中了。\n\n数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下\n\n```c\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F\n// 0 1 2 3\t4\t 5\t 6 7 8 9 \n0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x40, 0x00\n//A B C D E F H L - 熄灭\n```\n\n\n\n### 数码管静态显示\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管位选\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110 选通最左边的数码管\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\tP0 = 0X5b;//1101 1011 显示“1”\n\tDU = 0;//锁存段选数据\n\t\n\twhile(1);\n} \n```\n\n\n\n### 数码管动态显示\n\n要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n*8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即**快速扫描**每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示\n\n\n\n这个代码用来控制3个数码管来显示一个三位数\n\n```c\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n//设计一个函数用于显示三位的数字number\nvoid display(uchar number)\n{\n\tuchar bai, shi, ge;\n\tbai = number / 100; \t\t//百位\n\tshi = number % 100 / 10;\t//十位\n\tge = number % 10;\t\t\t//个位\n\t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\nvoid main()//main函数自身会循环\n{\t\n\twhile(1)\n\t{\n\t\tdisplay(185); //数码管显示185\n\t}\t\n} \n```\n\n\n\n\n\n\n\n## 键盘\n\n非编码键盘分为独立键盘和矩阵键盘\n\n### 独立键盘\n\n\n\n![image-20210821222715524](https://gitee.com/ajream/images/raw/master/img/20210821222718_image-20210821222715524.png)\n\n对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测\n\nP30是否为低电平即可\n\n> 【注意】\n>\n> 1. 按键消抖,最简单的是延时10~20ms后再检查其状态是否变化\n> 2. 松手检测\n\n```c\n/*********************************************************************************\n \t按下开发板S2按键数码管值+1,最大到9\n\t按下S3按下,值-1,最小减到0\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\n\n//毫秒级延时函数定义\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 9)//如果值不等于9则+1,功能把值限定为小于9\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\t//如果大于0则执行减一\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t\t//松手之后刷新显示\n\t\tDU = 1;//打开段选锁存器\n\t\tP0 = tabel[num];//\n\t\tDU = 0;//锁存段选数据\n\t}\t\n} \n```\n\n\n\n### 矩阵键盘\n\n判断矩阵按键中某个按键是否按下可采用**先列扫描再行扫描**的方式\n\n1. 列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了;\n2. 行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。\n\n\n\n```c\n/*********************************************************************************\n* 【程序功能】: \t4*4矩阵键盘与4位独立键盘识别\t\t \t\t\t \t\t\t \n* 【使用说明】: \t按下矩阵键盘和独立键盘任意键,数码管显示相应数值\n\t\t\t\t 初始显示“-”横\n**********************************************************************************/\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\nuchar KeyValue = 20;//按键值 显示-\n\n//共阴数码管段选表\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x37, 0x3E, 0x40, 0x00 }; \n//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,H,L,,n,u,-,熄灭\n\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid KeyScan()\n{\n\t//4*4矩阵键盘扫描\n\tP3 = 0XF0;//列扫描\n\tif(P3 != 0XF0)//判断按键是否被按下\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XF0)//判断按键是否被按下\n\t\t{\n\t\t\tswitch(P3) //判断那一列被按下\n\t\t\t{\n\t\t\t\tcase 0xe0:\tKeyValue = 0;\tbreak;//第一列被按下\n\t\t\t\tcase 0xd0:\tKeyValue = 1;\tbreak;//第二列被按下\n\t\t\t\tcase 0xb0:\tKeyValue = 2;\tbreak;//第三列被按下\n\t\t\t\tcase 0x70:\tKeyValue = 3;\tbreak;//第四列被按下 \n\t\t\t}\n\t\t\tP3 = 0X0F;//行扫描\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0x0e:\tKeyValue = KeyValue;\tbreak;//第一行被按下\n\t\t\t\tcase 0x0d:\tKeyValue = KeyValue + 4;\tbreak;//第二行被按下\n\t\t\t\tcase 0x0b:\tKeyValue = KeyValue + 8;\tbreak;//第三行被按下\n\t\t\t\tcase 0x07:\tKeyValue = KeyValue + 12;\tbreak;//第四行被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0X0F);//松手检测\t\n\t\t}\n\t}\n \n\tP3 = 0XFF;//独立按键扫描\n\tif(P3 != 0XFF)\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XFF)\n\t\t{\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0xfe:\tKeyValue = 16;\tbreak;//S2被按下\n\t\t\t\tcase 0xfd:\tKeyValue = 17;\tbreak;//S3被按下\n\t\t\t\tcase 0xfb:\tKeyValue = 18;\tbreak;//S4被按下\n\t\t\t\tcase 0xf7:\tKeyValue = 19;\tbreak;//S5被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0XFF);//松手检测\t\t\t\n\t\t}\t\n\t}\n\n}\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\twhile(1)\n\t{\n\t\tKeyScan();//20个按键键盘扫描\n\t\tP0 = tabel[KeyValue];//显示按键值\n\t}\t\n} \n```\n\n\n\n> 总结:先列扫描,再行扫描;或者先行扫描,再列扫描\n\n\n\n\n\n","slug":"51单片机/51单片机学习笔记(一)","published":1,"updated":"2021-09-10T12:45:53.371Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6nw0001akve96rfdnwk","content":"

点亮第一个led灯

    \n
  1. 新建工程

    \n

    \"image-20210821203338653\"

    \n
  2. \n
  3. 选择固件

    \n

    \"image-20210821203543242\"

    \n

    \"image-20210821203717166\"

    \n
  4. \n
  5. 新建代码源文件

    \n

    \"image-20210821203932209\"

    \n

    \"image-20210821204130367\"

    \n

    \"image-20210821204227781\"

    \n
  6. \n
  7. 添加如下代码

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    #include <reg52.h> //引用51头文件

    sbit LED = P1^1;

    void main()
    {
    \tLED = 0;\t//点亮LED2\t
    }
  8. \n
  9. 编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面

    \n

    \"image-20210821204723747\"

    \n
  10. \n
  11. 编译

    \n

    \"image-20210821205109242\"

    \n
  12. \n
  13. 上传(烧录)

    \n

    \"image-20210821205434569\"

    \n
  14. \n
\n

到此第一个点灯小程序结束

\n

流水灯

按照之前的步骤再建立一个新工程,写下如下代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <reg52.h>\t //包含51头文件
#include <intrins.h> //包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

uchar temp; //LED灯相关变量

//延时函数
void delay(uint z)
{
\tuint x, y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()
{
\ttemp = 0xfe;
\tP1 = temp; //1111 1110 初值LED1亮
\tdelay(1000);//毫秒级延时 100毫秒
\twhile(1)
\t{
\t\ttemp = _cror_(temp, 1);//循环左移
\t\tP1 = temp;//移位完成后赋值给P1 每个一个灯点亮
\t\tdelay(1000);//毫秒级延时 100毫秒
\t}\t
}
\n

说明:

\n
    \n
  • intrins.h 头文件包含移位函数 :右移_cror_()左移_corl_()
  • \n
\n

蜂鸣器

蜂鸣器分有源与无源蜂鸣器

\n
    \n
  • 有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音
  • \n
  • 无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响
  • \n
\n

一个蜂鸣器的简单驱动电路:

\n

\"image-20210821210804064\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <reg52.h>\t//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int

sbit beep = P2^3;//蜂鸣器

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()
{
\twhile(1)
\t{
\t\tbeep = ~beep;//\t蜂鸣器发出滴滴声
\t\tdelay(100);
\t}\t
}
\n

数码管

原理

单个数码管结构原理:

\n

\"image-20210821211542277\"

\n

多个数码管:

\n

\"image-20210821211818619\"

\n

说明:

\n

74HC573是个锁存器,原理如下:

\n

\"image-20210821212211630\"

\n

当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化

\n

当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态

\n

在这幅图

\n

\"image-20210821211818619\"

\n

用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符

\n

在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即1111 1110),最左边这个数码管就被选中了。

\n

数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下

\n
1
2
3
4
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
// 0 1 2 3\t4\t 5\t 6 7 8 9
0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x40, 0x00
//A B C D E F H L - 熄灭
\n

数码管静态显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管位选

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110 选通最左边的数码管
\tWE = 0;//锁存位选数据

\tDU = 1;//打开段选锁存器
\tP0 = 0X5b;//1101 1011 显示“1”
\tDU = 0;//锁存段选数据
\t
\twhile(1);
}
\n

数码管动态显示

要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即*快速扫描每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示

\n

这个代码用来控制3个数码管来显示一个三位数

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <reg52.h>//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

//设计一个函数用于显示三位的数字number
void display(uchar number)
{
\tuchar bai, shi, ge;
\tbai = number / 100; \t\t//百位
\tshi = number % 100 / 10;\t//十位
\tge = number % 10;\t\t\t//个位
\t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[bai];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第二位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFD; //1111 1101
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[shi];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第三位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFB; //1111 1011
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[ge];//
\tDU = 0;//锁存段选数据
\tdelay(5);
}

void main()//main函数自身会循环
{\t
\twhile(1)
\t{
\t\tdisplay(185); //数码管显示185
\t}\t
}
\n

键盘

非编码键盘分为独立键盘和矩阵键盘

\n

独立键盘

\"image-20210821222715524\"

\n

对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测

\n

P30是否为低电平即可

\n
\n

【注意】

\n
    \n
  1. 按键消抖,最简单的是延时10~20ms后再检查其状态是否变化
  2. \n
  3. 松手检测
  4. \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*********************************************************************************
\t按下开发板S2按键数码管值+1,最大到9
\t按下S3按下,值-1,最小减到0
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};


//毫秒级延时函数定义
void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\twhile(1)
\t{
\t\tif(key_s2 == 0)//判断S2是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s2 == 0)
\t\t\t{
\t\t\t\tif(num != 9)//如果值不等于9则+1,功能把值限定为小于9
\t\t\t\tnum++;
\t\t\t\twhile(!key_s2);//松手检测
\t\t\t}\t
\t\t}
\t\tif(key_s3 == 0)//判断S3是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s3 == 0)
\t\t\t{
\t\t\t\tif(num > 0)\t//如果大于0则执行减一
\t\t\t\t\tnum--;
\t\t\t\twhile(!key_s3);//松手检测
\t\t\t}\t
\t\t}
\t\t//松手之后刷新显示
\t\tDU = 1;//打开段选锁存器
\t\tP0 = tabel[num];//
\t\tDU = 0;//锁存段选数据
\t}\t
}
\n

矩阵键盘

判断矩阵按键中某个按键是否按下可采用先列扫描再行扫描的方式

\n
    \n
  1. 列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了;
  2. \n
  3. 行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*********************************************************************************
* 【程序功能】: \t4*4矩阵键盘与4位独立键盘识别\t\t \t\t\t \t\t\t
* 【使用说明】: \t按下矩阵键盘和独立键盘任意键,数码管显示相应数值
\t\t\t\t 初始显示“-”横
**********************************************************************************/
#include <reg52.h>//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
uchar num;//数码管显示的值
uchar KeyValue = 20;//按键值 显示-

//共阴数码管段选表
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x37, 0x3E, 0x40, 0x00 };
//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,H,L,,n,u,-,熄灭


void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void KeyScan()
{
\t//4*4矩阵键盘扫描
\tP3 = 0XF0;//列扫描
\tif(P3 != 0XF0)//判断按键是否被按下
\t{
\t\tdelay(10);//软件消抖10ms
\t\tif(P3 != 0XF0)//判断按键是否被按下
\t\t{
\t\t\tswitch(P3) //判断那一列被按下
\t\t\t{
\t\t\t\tcase 0xe0:\tKeyValue = 0;\tbreak;//第一列被按下
\t\t\t\tcase 0xd0:\tKeyValue = 1;\tbreak;//第二列被按下
\t\t\t\tcase 0xb0:\tKeyValue = 2;\tbreak;//第三列被按下
\t\t\t\tcase 0x70:\tKeyValue = 3;\tbreak;//第四列被按下
\t\t\t}
\t\t\tP3 = 0X0F;//行扫描
\t\t\tswitch(P3) //判断那一行被按下
\t\t\t{
\t\t\t\tcase 0x0e:\tKeyValue = KeyValue;\tbreak;//第一行被按下
\t\t\t\tcase 0x0d:\tKeyValue = KeyValue + 4;\tbreak;//第二行被按下
\t\t\t\tcase 0x0b:\tKeyValue = KeyValue + 8;\tbreak;//第三行被按下
\t\t\t\tcase 0x07:\tKeyValue = KeyValue + 12;\tbreak;//第四行被按下
\t\t\t}
\t\t\twhile(P3 != 0X0F);//松手检测\t
\t\t}
\t}

\tP3 = 0XFF;//独立按键扫描
\tif(P3 != 0XFF)
\t{
\t\tdelay(10);//软件消抖10ms
\t\tif(P3 != 0XFF)
\t\t{
\t\t\tswitch(P3) //判断那一行被按下
\t\t\t{
\t\t\t\tcase 0xfe:\tKeyValue = 16;\tbreak;//S2被按下
\t\t\t\tcase 0xfd:\tKeyValue = 17;\tbreak;//S3被按下
\t\t\t\tcase 0xfb:\tKeyValue = 18;\tbreak;//S4被按下
\t\t\t\tcase 0xf7:\tKeyValue = 19;\tbreak;//S5被按下
\t\t\t}
\t\t\twhile(P3 != 0XFF);//松手检测\t\t\t
\t\t}\t
\t}

}

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据

\tDU = 1;//打开段选锁存器
\twhile(1)
\t{
\t\tKeyScan();//20个按键键盘扫描
\t\tP0 = tabel[KeyValue];//显示按键值
\t}\t
}
\n
\n

总结:先列扫描,再行扫描;或者先行扫描,再列扫描

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

点亮第一个led灯

    \n
  1. 新建工程

    \n

    \"image-20210821203338653\"

    \n
  2. \n
  3. 选择固件

    \n

    \"image-20210821203543242\"

    \n

    \"image-20210821203717166\"

    \n
  4. \n
  5. 新建代码源文件

    \n

    \"image-20210821203932209\"

    \n

    \"image-20210821204130367\"

    \n

    \"image-20210821204227781\"

    \n
  6. \n
  7. 添加如下代码

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    #include <reg52.h> //引用51头文件

    sbit LED = P1^1;

    void main()
    {
    \tLED = 0;\t//点亮LED2\t
    }
  8. \n
  9. 编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面

    \n

    \"image-20210821204723747\"

    \n
  10. \n
  11. 编译

    \n

    \"image-20210821205109242\"

    \n
  12. \n
  13. 上传(烧录)

    \n

    \"image-20210821205434569\"

    \n
  14. \n
\n

到此第一个点灯小程序结束

\n

流水灯

按照之前的步骤再建立一个新工程,写下如下代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <reg52.h>\t //包含51头文件
#include <intrins.h> //包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

uchar temp; //LED灯相关变量

//延时函数
void delay(uint z)
{
\tuint x, y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()
{
\ttemp = 0xfe;
\tP1 = temp; //1111 1110 初值LED1亮
\tdelay(1000);//毫秒级延时 100毫秒
\twhile(1)
\t{
\t\ttemp = _cror_(temp, 1);//循环左移
\t\tP1 = temp;//移位完成后赋值给P1 每个一个灯点亮
\t\tdelay(1000);//毫秒级延时 100毫秒
\t}\t
}
\n

说明:

\n
    \n
  • intrins.h 头文件包含移位函数 :右移_cror_()左移_corl_()
  • \n
\n

蜂鸣器

蜂鸣器分有源与无源蜂鸣器

\n
    \n
  • 有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音
  • \n
  • 无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响
  • \n
\n

一个蜂鸣器的简单驱动电路:

\n

\"image-20210821210804064\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <reg52.h>\t//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int

sbit beep = P2^3;//蜂鸣器

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()
{
\twhile(1)
\t{
\t\tbeep = ~beep;//\t蜂鸣器发出滴滴声
\t\tdelay(100);
\t}\t
}
\n

数码管

原理

单个数码管结构原理:

\n

\"image-20210821211542277\"

\n

多个数码管:

\n

\"image-20210821211818619\"

\n

说明:

\n

74HC573是个锁存器,原理如下:

\n

\"image-20210821212211630\"

\n

当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化

\n

当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态

\n

在这幅图

\n

\"image-20210821211818619\"

\n

用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符

\n

在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即1111 1110),最左边这个数码管就被选中了。

\n

数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下

\n
1
2
3
4
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
// 0 1 2 3\t4\t 5\t 6 7 8 9
0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x40, 0x00
//A B C D E F H L - 熄灭
\n

数码管静态显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管位选

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110 选通最左边的数码管
\tWE = 0;//锁存位选数据

\tDU = 1;//打开段选锁存器
\tP0 = 0X5b;//1101 1011 显示“1”
\tDU = 0;//锁存段选数据
\t
\twhile(1);
}
\n

数码管动态显示

要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即*快速扫描每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示

\n

这个代码用来控制3个数码管来显示一个三位数

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <reg52.h>//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

//设计一个函数用于显示三位的数字number
void display(uchar number)
{
\tuchar bai, shi, ge;
\tbai = number / 100; \t\t//百位
\tshi = number % 100 / 10;\t//十位
\tge = number % 10;\t\t\t//个位
\t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[bai];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第二位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFD; //1111 1101
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[shi];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第三位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFB; //1111 1011
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[ge];//
\tDU = 0;//锁存段选数据
\tdelay(5);
}

void main()//main函数自身会循环
{\t
\twhile(1)
\t{
\t\tdisplay(185); //数码管显示185
\t}\t
}
\n

键盘

非编码键盘分为独立键盘和矩阵键盘

\n

独立键盘

\"image-20210821222715524\"

\n

对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测

\n

P30是否为低电平即可

\n
\n

【注意】

\n
    \n
  1. 按键消抖,最简单的是延时10~20ms后再检查其状态是否变化
  2. \n
  3. 松手检测
  4. \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*********************************************************************************
\t按下开发板S2按键数码管值+1,最大到9
\t按下S3按下,值-1,最小减到0
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};


//毫秒级延时函数定义
void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\twhile(1)
\t{
\t\tif(key_s2 == 0)//判断S2是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s2 == 0)
\t\t\t{
\t\t\t\tif(num != 9)//如果值不等于9则+1,功能把值限定为小于9
\t\t\t\tnum++;
\t\t\t\twhile(!key_s2);//松手检测
\t\t\t}\t
\t\t}
\t\tif(key_s3 == 0)//判断S3是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s3 == 0)
\t\t\t{
\t\t\t\tif(num > 0)\t//如果大于0则执行减一
\t\t\t\t\tnum--;
\t\t\t\twhile(!key_s3);//松手检测
\t\t\t}\t
\t\t}
\t\t//松手之后刷新显示
\t\tDU = 1;//打开段选锁存器
\t\tP0 = tabel[num];//
\t\tDU = 0;//锁存段选数据
\t}\t
}
\n

矩阵键盘

判断矩阵按键中某个按键是否按下可采用先列扫描再行扫描的方式

\n
    \n
  1. 列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了;
  2. \n
  3. 行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*********************************************************************************
* 【程序功能】: \t4*4矩阵键盘与4位独立键盘识别\t\t \t\t\t \t\t\t
* 【使用说明】: \t按下矩阵键盘和独立键盘任意键,数码管显示相应数值
\t\t\t\t 初始显示“-”横
**********************************************************************************/
#include <reg52.h>//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
uchar num;//数码管显示的值
uchar KeyValue = 20;//按键值 显示-

//共阴数码管段选表
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x37, 0x3E, 0x40, 0x00 };
//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,H,L,,n,u,-,熄灭


void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void KeyScan()
{
\t//4*4矩阵键盘扫描
\tP3 = 0XF0;//列扫描
\tif(P3 != 0XF0)//判断按键是否被按下
\t{
\t\tdelay(10);//软件消抖10ms
\t\tif(P3 != 0XF0)//判断按键是否被按下
\t\t{
\t\t\tswitch(P3) //判断那一列被按下
\t\t\t{
\t\t\t\tcase 0xe0:\tKeyValue = 0;\tbreak;//第一列被按下
\t\t\t\tcase 0xd0:\tKeyValue = 1;\tbreak;//第二列被按下
\t\t\t\tcase 0xb0:\tKeyValue = 2;\tbreak;//第三列被按下
\t\t\t\tcase 0x70:\tKeyValue = 3;\tbreak;//第四列被按下
\t\t\t}
\t\t\tP3 = 0X0F;//行扫描
\t\t\tswitch(P3) //判断那一行被按下
\t\t\t{
\t\t\t\tcase 0x0e:\tKeyValue = KeyValue;\tbreak;//第一行被按下
\t\t\t\tcase 0x0d:\tKeyValue = KeyValue + 4;\tbreak;//第二行被按下
\t\t\t\tcase 0x0b:\tKeyValue = KeyValue + 8;\tbreak;//第三行被按下
\t\t\t\tcase 0x07:\tKeyValue = KeyValue + 12;\tbreak;//第四行被按下
\t\t\t}
\t\t\twhile(P3 != 0X0F);//松手检测\t
\t\t}
\t}

\tP3 = 0XFF;//独立按键扫描
\tif(P3 != 0XFF)
\t{
\t\tdelay(10);//软件消抖10ms
\t\tif(P3 != 0XFF)
\t\t{
\t\t\tswitch(P3) //判断那一行被按下
\t\t\t{
\t\t\t\tcase 0xfe:\tKeyValue = 16;\tbreak;//S2被按下
\t\t\t\tcase 0xfd:\tKeyValue = 17;\tbreak;//S3被按下
\t\t\t\tcase 0xfb:\tKeyValue = 18;\tbreak;//S4被按下
\t\t\t\tcase 0xf7:\tKeyValue = 19;\tbreak;//S5被按下
\t\t\t}
\t\t\twhile(P3 != 0XFF);//松手检测\t\t\t
\t\t}\t
\t}

}

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据

\tDU = 1;//打开段选锁存器
\twhile(1)
\t{
\t\tKeyScan();//20个按键键盘扫描
\t\tP0 = tabel[KeyValue];//显示按键值
\t}\t
}
\n
\n

总结:先列扫描,再行扫描;或者先行扫描,再列扫描

\n
\n"},{"title":"51单片机学习笔记(七)","description":"红外通信","abbrlink":"c2d1d169","date":"2021-08-20T08:32:44.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n## 红外通信\n\n### 红外遥控电路组成\n\n一般而言,红外遥控系统由发射装置接收装置两大部分组成\n\n\n\n发射装置(例如遥控器)主要包括:\n\n- 键盘电路\n- 红外编码芯片\n- 电源\n- 红外发射电路\n\n\n\n红外接收设备:\n\n- 可由红外接收电路\n- 红外解码芯片\n- 电源\n- 应用电路组成\n\n\n\n![image-20210824165320963](https://gitee.com/ajream/images/raw/master/img/20210824165322_image-20210824165320963.png)\n\n\n\n\n\n### 信号调制和解调\n\n\n\n为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号\n\n\n\n\n\n### NEC协议\n\n\n\nNEC 标准下的编码表示\n\n1. 引导码高电平约 9000us 左右,低电平约4500us 左右\n\n2. 用户码16 位,数据码16 位,共32位\n\n ![image-20210824165820317](https://gitee.com/ajream/images/raw/master/img/20210824165821_image-20210824165820317.png)\n\n \n\n3. 数据0 是用“高电平约 560us + 低电平约 560us ”表示\n\n4. 数据1 是用“高电平约 560us + 低电平约 1680us”表示\n\n ![image-20210824165845177](https://gitee.com/ajream/images/raw/master/img/20210824165846_image-20210824165845177.png)\n\n\n\n\n\n\n\n## 例子\n\n\n\n```c\n/*\nNEC协议红外通信\n单片机解码后通过串口以9600的比特率发送出去\n*/\n\n#include \n\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit IR = P3^2; //定义红外脉冲数据接口\t外部中断O输入口\n\nuchar IRtime; \t\t//检测红外高电平持续时间(脉宽)\nuchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)\nuchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)\nbit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕\n\nvoid init()\t //初始化定时器0 和外部中断0\n{\n\tTMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装\n\tTH0 = 0x00; //高8位装入0那么定时器溢出一次的时间是256个机器周期\n\tTL0 = 0x00;\n\tEA = 1; //总中断\n\tET0 = 1;\t //定时器0中断\n\tTR0 = 1; //启动定时器0\n\n\tIT0 = 1;\t //设置外部中断0为跳沿触发方式,来一个下降沿触发一次\n\tEX0 = 1;\t //启动外部中断0\n\n\tTH1 = 0xfd; //此溢出率为波特率9600\n\tTL1 = 0xfd;\n\tTR1 = 1; //启动定时器1\n\tSM1 = 1; //设置串口工作方式1,10位异步收发器\n}\n\nvoid time0() interrupt 1 //定义定时器0\n{\n\tIRtime++; \t\t\t //检测脉宽,1次为278us\n}\n\nvoid int0() interrupt 0\t \t\t//定义外部中断0\n{\n\tstatic uchar i;\t \t\t\t//\t声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata\n\tstatic bit startflag;\t\t//开始储存脉宽标志位\n\tif(startflag)\t \t\t\t//开始接收脉宽检测\n\t{\n\t\tif( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us\t\n\t\t这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000 \n\t\t如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/\n\t\t\ti = 0;\t\t\t\t //如果是引导码那么执行i=0把他存到IRdata的第一个位\n\t\tIRdata[i] = IRtime; \t\t //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断\n\t\tIRtime = 0;\t\t\t\t //计数清零,下一个下降沿的时候在存入脉宽\n\t\ti++; \t\t\t\t\t //计数脉宽存入的次数\n\t\tif(i == 33) \t\t\t\t //如果存入34次 数组的下标是从0开始i等于33表示执行了34次\n\t\t{\n\t\t \tIRok = 1;\t\t\t\t //那么表示脉宽检测完毕\n\t\t\ti = 0; \t\t\t\t //把脉宽计数清零准备下次存入\n\t\t}\n\t}\n\telse\t\t \n\t{\n\t\tIRtime = 0; \t\t\t\t //引导码开始进入把脉宽计数清零开始计数\n\t\tstartflag = 1;\t\t\t //开始处理标志位置1\n\t}\n}\n\nvoid IRcordpro() \t\t\t\t //提取它的33次脉宽进行数据解码\n{\n\tuchar i, j, k, cord, value;\t/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位\n\tcord用于取出脉宽的时间判断是否符合1的脉宽时间*/\n\tk = 1; \t\t\t\t\t\t//从第一位脉宽开始取,丢弃引导码脉宽\n\tfor(i = 0; i < 4; i++)\n\t{\n\t\tfor(j = 0; j < 8; j++)\n\t\t{\n\t\t\tcord = IRdata[k];\t //把脉宽存入cord\n\t\t\tif(cord > 5)\t \t\t//如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1\n\t\t\tvalue = value | 0x80;\t/*接收的时候是先接收最低位,\n\t\t\t把最低位先放到value的最高位在和0x08按位或一下\n\t\t\t这样不会改变valua的其他位的数值只会让他最高位为1*/\n\t\t\tif(j < 7)\n\t\t\t{\n\t\t\t\tvalue = value >> 1;\t//value位左移依次接收8位数据。\n\t\t\t}\n\t\t\tk++;\t\t\t\t//每执行一次脉宽位加1\n\t\t}\n\t\tIRcord[i] = value;\t //每处理完一个字节把它放入IRcord数组中。\n\t\tvalue = 0; \t\t\t //清零value方便下次在存入数据\n\t}\n\tIRpro_ok = 1;\t\t\t\t //接收完4个字节后IRpro ok置1表示红外解码完成\t\n}\n\n\nvoid main()\n{\n\tuchar i;\n\tinit();\t//执行初始化定时器0和外部中断0\n\twhile(1)\t//大循环\n\t{\n\t\tif(IRok) //判断脉宽是否检测完毕 \n\t\t{ \n\t\t\tIRcordpro();//根据脉宽解码出4个字节的数据\n\t\t\tIRok = 0;\t//重新等待脉宽检测\n\t\t\tif(IRpro_ok) //判断是否解码完毕 \n\t\t\t{\n\t\t\t\tfor(i = 0; i < 4; i++) \n\t\t\t\t{\t \n\t\t\t\t\tSBUF = IRcord[i];\n\t\t\t\t\twhile(!TI);\n\t\t\t\t\tTI = 0;\n\t\t\t\t}\n\t\t\t\tIRpro_ok = 0;\n\t\t\t}\n\t\t}\t\n\t}\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(七).md","raw":"---\ntitle: 51单片机学习笔记(七)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 红外通信\nabbrlink: c2d1d169\ndate: 2021-08-20 16:32:44\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n## 红外通信\n\n### 红外遥控电路组成\n\n一般而言,红外遥控系统由发射装置接收装置两大部分组成\n\n\n\n发射装置(例如遥控器)主要包括:\n\n- 键盘电路\n- 红外编码芯片\n- 电源\n- 红外发射电路\n\n\n\n红外接收设备:\n\n- 可由红外接收电路\n- 红外解码芯片\n- 电源\n- 应用电路组成\n\n\n\n![image-20210824165320963](https://gitee.com/ajream/images/raw/master/img/20210824165322_image-20210824165320963.png)\n\n\n\n\n\n### 信号调制和解调\n\n\n\n为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号\n\n\n\n\n\n### NEC协议\n\n\n\nNEC 标准下的编码表示\n\n1. 引导码高电平约 9000us 左右,低电平约4500us 左右\n\n2. 用户码16 位,数据码16 位,共32位\n\n ![image-20210824165820317](https://gitee.com/ajream/images/raw/master/img/20210824165821_image-20210824165820317.png)\n\n \n\n3. 数据0 是用“高电平约 560us + 低电平约 560us ”表示\n\n4. 数据1 是用“高电平约 560us + 低电平约 1680us”表示\n\n ![image-20210824165845177](https://gitee.com/ajream/images/raw/master/img/20210824165846_image-20210824165845177.png)\n\n\n\n\n\n\n\n## 例子\n\n\n\n```c\n/*\nNEC协议红外通信\n单片机解码后通过串口以9600的比特率发送出去\n*/\n\n#include \n\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit IR = P3^2; //定义红外脉冲数据接口\t外部中断O输入口\n\nuchar IRtime; \t\t//检测红外高电平持续时间(脉宽)\nuchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)\nuchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)\nbit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕\n\nvoid init()\t //初始化定时器0 和外部中断0\n{\n\tTMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装\n\tTH0 = 0x00; //高8位装入0那么定时器溢出一次的时间是256个机器周期\n\tTL0 = 0x00;\n\tEA = 1; //总中断\n\tET0 = 1;\t //定时器0中断\n\tTR0 = 1; //启动定时器0\n\n\tIT0 = 1;\t //设置外部中断0为跳沿触发方式,来一个下降沿触发一次\n\tEX0 = 1;\t //启动外部中断0\n\n\tTH1 = 0xfd; //此溢出率为波特率9600\n\tTL1 = 0xfd;\n\tTR1 = 1; //启动定时器1\n\tSM1 = 1; //设置串口工作方式1,10位异步收发器\n}\n\nvoid time0() interrupt 1 //定义定时器0\n{\n\tIRtime++; \t\t\t //检测脉宽,1次为278us\n}\n\nvoid int0() interrupt 0\t \t\t//定义外部中断0\n{\n\tstatic uchar i;\t \t\t\t//\t声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata\n\tstatic bit startflag;\t\t//开始储存脉宽标志位\n\tif(startflag)\t \t\t\t//开始接收脉宽检测\n\t{\n\t\tif( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us\t\n\t\t这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000 \n\t\t如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/\n\t\t\ti = 0;\t\t\t\t //如果是引导码那么执行i=0把他存到IRdata的第一个位\n\t\tIRdata[i] = IRtime; \t\t //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断\n\t\tIRtime = 0;\t\t\t\t //计数清零,下一个下降沿的时候在存入脉宽\n\t\ti++; \t\t\t\t\t //计数脉宽存入的次数\n\t\tif(i == 33) \t\t\t\t //如果存入34次 数组的下标是从0开始i等于33表示执行了34次\n\t\t{\n\t\t \tIRok = 1;\t\t\t\t //那么表示脉宽检测完毕\n\t\t\ti = 0; \t\t\t\t //把脉宽计数清零准备下次存入\n\t\t}\n\t}\n\telse\t\t \n\t{\n\t\tIRtime = 0; \t\t\t\t //引导码开始进入把脉宽计数清零开始计数\n\t\tstartflag = 1;\t\t\t //开始处理标志位置1\n\t}\n}\n\nvoid IRcordpro() \t\t\t\t //提取它的33次脉宽进行数据解码\n{\n\tuchar i, j, k, cord, value;\t/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位\n\tcord用于取出脉宽的时间判断是否符合1的脉宽时间*/\n\tk = 1; \t\t\t\t\t\t//从第一位脉宽开始取,丢弃引导码脉宽\n\tfor(i = 0; i < 4; i++)\n\t{\n\t\tfor(j = 0; j < 8; j++)\n\t\t{\n\t\t\tcord = IRdata[k];\t //把脉宽存入cord\n\t\t\tif(cord > 5)\t \t\t//如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1\n\t\t\tvalue = value | 0x80;\t/*接收的时候是先接收最低位,\n\t\t\t把最低位先放到value的最高位在和0x08按位或一下\n\t\t\t这样不会改变valua的其他位的数值只会让他最高位为1*/\n\t\t\tif(j < 7)\n\t\t\t{\n\t\t\t\tvalue = value >> 1;\t//value位左移依次接收8位数据。\n\t\t\t}\n\t\t\tk++;\t\t\t\t//每执行一次脉宽位加1\n\t\t}\n\t\tIRcord[i] = value;\t //每处理完一个字节把它放入IRcord数组中。\n\t\tvalue = 0; \t\t\t //清零value方便下次在存入数据\n\t}\n\tIRpro_ok = 1;\t\t\t\t //接收完4个字节后IRpro ok置1表示红外解码完成\t\n}\n\n\nvoid main()\n{\n\tuchar i;\n\tinit();\t//执行初始化定时器0和外部中断0\n\twhile(1)\t//大循环\n\t{\n\t\tif(IRok) //判断脉宽是否检测完毕 \n\t\t{ \n\t\t\tIRcordpro();//根据脉宽解码出4个字节的数据\n\t\t\tIRok = 0;\t//重新等待脉宽检测\n\t\t\tif(IRpro_ok) //判断是否解码完毕 \n\t\t\t{\n\t\t\t\tfor(i = 0; i < 4; i++) \n\t\t\t\t{\t \n\t\t\t\t\tSBUF = IRcord[i];\n\t\t\t\t\twhile(!TI);\n\t\t\t\t\tTI = 0;\n\t\t\t\t}\n\t\t\t\tIRpro_ok = 0;\n\t\t\t}\n\t\t}\t\n\t}\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(七)","published":1,"updated":"2021-09-10T12:45:26.554Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6nz0003akve5a7o3s93","content":"

红外通信

红外遥控电路组成

一般而言,红外遥控系统由发射装置接收装置两大部分组成

\n

发射装置(例如遥控器)主要包括:

\n
    \n
  • 键盘电路
  • \n
  • 红外编码芯片
  • \n
  • 电源
  • \n
  • 红外发射电路
  • \n
\n

红外接收设备:

\n
    \n
  • 可由红外接收电路
  • \n
  • 红外解码芯片
  • \n
  • 电源
  • \n
  • 应用电路组成
  • \n
\n

\"image-20210824165320963\"

\n

信号调制和解调

为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号

\n

NEC协议

NEC 标准下的编码表示

\n
    \n
  1. 引导码高电平约 9000us 左右,低电平约4500us 左右

    \n
  2. \n
  3. 用户码16 位,数据码16 位,共32位

    \n

    \"image-20210824165820317\"

    \n
  4. \n
\n
    \n
  1. 数据0 是用“高电平约 560us + 低电平约 560us ”表示

    \n
  2. \n
  3. 数据1 是用“高电平约 560us + 低电平约 1680us”表示

    \n

    \"image-20210824165845177\"

    \n
  4. \n
\n

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
NEC协议红外通信
单片机解码后通过串口以9600的比特率发送出去
*/

#include <reg52.h>

/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit IR = P3^2; //定义红外脉冲数据接口\t外部中断O输入口

uchar IRtime; \t\t//检测红外高电平持续时间(脉宽)
uchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)
uchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)
bit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕

void init()\t //初始化定时器0 和外部中断0
{
\tTMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装
\tTH0 = 0x00; //高8位装入0那么定时器溢出一次的时间是256个机器周期
\tTL0 = 0x00;
\tEA = 1; //总中断
\tET0 = 1;\t //定时器0中断
\tTR0 = 1; //启动定时器0

\tIT0 = 1;\t //设置外部中断0为跳沿触发方式,来一个下降沿触发一次
\tEX0 = 1;\t //启动外部中断0

\tTH1 = 0xfd; //此溢出率为波特率9600
\tTL1 = 0xfd;
\tTR1 = 1; //启动定时器1
\tSM1 = 1; //设置串口工作方式1,10位异步收发器
}

void time0() interrupt 1 //定义定时器0
{
\tIRtime++; \t\t\t //检测脉宽,1次为278us
}

void int0() interrupt 0\t \t\t//定义外部中断0
{
\tstatic uchar i;\t \t\t\t//\t声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata
\tstatic bit startflag;\t\t//开始储存脉宽标志位
\tif(startflag)\t \t\t\t//开始接收脉宽检测
\t{
\t\tif( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us\t
\t\t这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000
\t\t如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/
\t\t\ti = 0;\t\t\t\t //如果是引导码那么执行i=0把他存到IRdata的第一个位
\t\tIRdata[i] = IRtime; \t\t //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断
\t\tIRtime = 0;\t\t\t\t //计数清零,下一个下降沿的时候在存入脉宽
\t\ti++; \t\t\t\t\t //计数脉宽存入的次数
\t\tif(i == 33) \t\t\t\t //如果存入34次 数组的下标是从0开始i等于33表示执行了34次
\t\t{
\t\t \tIRok = 1;\t\t\t\t //那么表示脉宽检测完毕
\t\t\ti = 0; \t\t\t\t //把脉宽计数清零准备下次存入
\t\t}
\t}
\telse\t\t
\t{
\t\tIRtime = 0; \t\t\t\t //引导码开始进入把脉宽计数清零开始计数
\t\tstartflag = 1;\t\t\t //开始处理标志位置1
\t}
}

void IRcordpro() \t\t\t\t //提取它的33次脉宽进行数据解码
{
\tuchar i, j, k, cord, value;\t/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位
\tcord用于取出脉宽的时间判断是否符合1的脉宽时间*/
\tk = 1; \t\t\t\t\t\t//从第一位脉宽开始取,丢弃引导码脉宽
\tfor(i = 0; i < 4; i++)
\t{
\t\tfor(j = 0; j < 8; j++)
\t\t{
\t\t\tcord = IRdata[k];\t //把脉宽存入cord
\t\t\tif(cord > 5)\t \t\t//如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1
\t\t\tvalue = value | 0x80;\t/*接收的时候是先接收最低位,
\t\t\t把最低位先放到value的最高位在和0x08按位或一下
\t\t\t这样不会改变valua的其他位的数值只会让他最高位为1*/
\t\t\tif(j < 7)
\t\t\t{
\t\t\t\tvalue = value >> 1;\t//value位左移依次接收8位数据。
\t\t\t}
\t\t\tk++;\t\t\t\t//每执行一次脉宽位加1
\t\t}
\t\tIRcord[i] = value;\t //每处理完一个字节把它放入IRcord数组中。
\t\tvalue = 0; \t\t\t //清零value方便下次在存入数据
\t}
\tIRpro_ok = 1;\t\t\t\t //接收完4个字节后IRpro ok置1表示红外解码完成\t
}


void main()
{
\tuchar i;
\tinit();\t//执行初始化定时器0和外部中断0
\twhile(1)\t//大循环
\t{
\t\tif(IRok) //判断脉宽是否检测完毕
\t\t{
\t\t\tIRcordpro();//根据脉宽解码出4个字节的数据
\t\t\tIRok = 0;\t//重新等待脉宽检测
\t\t\tif(IRpro_ok) //判断是否解码完毕
\t\t\t{
\t\t\t\tfor(i = 0; i < 4; i++)
\t\t\t\t{\t
\t\t\t\t\tSBUF = IRcord[i];
\t\t\t\t\twhile(!TI);
\t\t\t\t\tTI = 0;
\t\t\t\t}
\t\t\t\tIRpro_ok = 0;
\t\t\t}
\t\t}\t
\t}
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

红外通信

红外遥控电路组成

一般而言,红外遥控系统由发射装置接收装置两大部分组成

\n

发射装置(例如遥控器)主要包括:

\n
    \n
  • 键盘电路
  • \n
  • 红外编码芯片
  • \n
  • 电源
  • \n
  • 红外发射电路
  • \n
\n

红外接收设备:

\n
    \n
  • 可由红外接收电路
  • \n
  • 红外解码芯片
  • \n
  • 电源
  • \n
  • 应用电路组成
  • \n
\n

\"image-20210824165320963\"

\n

信号调制和解调

为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号

\n

NEC协议

NEC 标准下的编码表示

\n
    \n
  1. 引导码高电平约 9000us 左右,低电平约4500us 左右

    \n
  2. \n
  3. 用户码16 位,数据码16 位,共32位

    \n

    \"image-20210824165820317\"

    \n
  4. \n
\n
    \n
  1. 数据0 是用“高电平约 560us + 低电平约 560us ”表示

    \n
  2. \n
  3. 数据1 是用“高电平约 560us + 低电平约 1680us”表示

    \n

    \"image-20210824165845177\"

    \n
  4. \n
\n

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
NEC协议红外通信
单片机解码后通过串口以9600的比特率发送出去
*/

#include <reg52.h>

/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit IR = P3^2; //定义红外脉冲数据接口\t外部中断O输入口

uchar IRtime; \t\t//检测红外高电平持续时间(脉宽)
uchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)
uchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)
bit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕

void init()\t //初始化定时器0 和外部中断0
{
\tTMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装
\tTH0 = 0x00; //高8位装入0那么定时器溢出一次的时间是256个机器周期
\tTL0 = 0x00;
\tEA = 1; //总中断
\tET0 = 1;\t //定时器0中断
\tTR0 = 1; //启动定时器0

\tIT0 = 1;\t //设置外部中断0为跳沿触发方式,来一个下降沿触发一次
\tEX0 = 1;\t //启动外部中断0

\tTH1 = 0xfd; //此溢出率为波特率9600
\tTL1 = 0xfd;
\tTR1 = 1; //启动定时器1
\tSM1 = 1; //设置串口工作方式1,10位异步收发器
}

void time0() interrupt 1 //定义定时器0
{
\tIRtime++; \t\t\t //检测脉宽,1次为278us
}

void int0() interrupt 0\t \t\t//定义外部中断0
{
\tstatic uchar i;\t \t\t\t//\t声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata
\tstatic bit startflag;\t\t//开始储存脉宽标志位
\tif(startflag)\t \t\t\t//开始接收脉宽检测
\t{
\t\tif( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us\t
\t\t这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000
\t\t如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/
\t\t\ti = 0;\t\t\t\t //如果是引导码那么执行i=0把他存到IRdata的第一个位
\t\tIRdata[i] = IRtime; \t\t //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断
\t\tIRtime = 0;\t\t\t\t //计数清零,下一个下降沿的时候在存入脉宽
\t\ti++; \t\t\t\t\t //计数脉宽存入的次数
\t\tif(i == 33) \t\t\t\t //如果存入34次 数组的下标是从0开始i等于33表示执行了34次
\t\t{
\t\t \tIRok = 1;\t\t\t\t //那么表示脉宽检测完毕
\t\t\ti = 0; \t\t\t\t //把脉宽计数清零准备下次存入
\t\t}
\t}
\telse\t\t
\t{
\t\tIRtime = 0; \t\t\t\t //引导码开始进入把脉宽计数清零开始计数
\t\tstartflag = 1;\t\t\t //开始处理标志位置1
\t}
}

void IRcordpro() \t\t\t\t //提取它的33次脉宽进行数据解码
{
\tuchar i, j, k, cord, value;\t/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位
\tcord用于取出脉宽的时间判断是否符合1的脉宽时间*/
\tk = 1; \t\t\t\t\t\t//从第一位脉宽开始取,丢弃引导码脉宽
\tfor(i = 0; i < 4; i++)
\t{
\t\tfor(j = 0; j < 8; j++)
\t\t{
\t\t\tcord = IRdata[k];\t //把脉宽存入cord
\t\t\tif(cord > 5)\t \t\t//如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1
\t\t\tvalue = value | 0x80;\t/*接收的时候是先接收最低位,
\t\t\t把最低位先放到value的最高位在和0x08按位或一下
\t\t\t这样不会改变valua的其他位的数值只会让他最高位为1*/
\t\t\tif(j < 7)
\t\t\t{
\t\t\t\tvalue = value >> 1;\t//value位左移依次接收8位数据。
\t\t\t}
\t\t\tk++;\t\t\t\t//每执行一次脉宽位加1
\t\t}
\t\tIRcord[i] = value;\t //每处理完一个字节把它放入IRcord数组中。
\t\tvalue = 0; \t\t\t //清零value方便下次在存入数据
\t}
\tIRpro_ok = 1;\t\t\t\t //接收完4个字节后IRpro ok置1表示红外解码完成\t
}


void main()
{
\tuchar i;
\tinit();\t//执行初始化定时器0和外部中断0
\twhile(1)\t//大循环
\t{
\t\tif(IRok) //判断脉宽是否检测完毕
\t\t{
\t\t\tIRcordpro();//根据脉宽解码出4个字节的数据
\t\t\tIRok = 0;\t//重新等待脉宽检测
\t\t\tif(IRpro_ok) //判断是否解码完毕
\t\t\t{
\t\t\t\tfor(i = 0; i < 4; i++)
\t\t\t\t{\t
\t\t\t\t\tSBUF = IRcord[i];
\t\t\t\t\twhile(!TI);
\t\t\t\t\tTI = 0;
\t\t\t\t}
\t\t\t\tIRpro_ok = 0;
\t\t\t}
\t\t}\t
\t}
}
\n"},{"title":"51单片机学习笔记(九)","description":"LCD1602液晶显示器基本操作使用","abbrlink":"17528481","date":"2021-08-21T06:32:19.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n## 简单介绍\n\n### 主要参数\n\n显示容量:16X2个字符\n\n芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)\n\n\n\n\n\n### 引脚说明\n\nRS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个\"格子\");\n\n​\t为1时表示数据输入,即每个”格子“显示的内容\n\nR/W:为1时是读,0是写;\n\nE:读写数据时要置1\n\nD0~D7:数据输入输出\n\n![image-20210825154248774](https://gitee.com/ajream/images/raw/master/img/20210825154252_image-20210825154248774.png)\n\n\n\n\n\n## 基本操作\n\n### 读写操作\n\n- 读状态,输入:RS=0, RW=1, E=0; \n\n 此时输出数据D0~D7为状态字,每一位都有各自的状态;\n\n 其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;\n\n ![image-20210825155602088](https://gitee.com/ajream/images/raw/master/img/20210825155603_image-20210825155602088.png)\n\n- 写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码\n- 读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据\n- 写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲\n\n\n\n```c\nvoid Read_Busy() //读状态判断是芯片否处于”忙“状态\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n Read_Busy();//判断忙\n RS = 0;\n RW = 0;\n P0 = cmd;\n EN = 1;\n EN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\n```\n\n\n\n\n\n### RAM地址映射\n\n控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:\n\n![image-20210825160547241](https://gitee.com/ajream/images/raw/master/img/20210825160548_image-20210825160547241.png)\n\n注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符\n\n\n\n### 指令说明\n\n初始化设置:\n\n1. 显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口\n\n2. 开/关光标设置:(配置时要转为16进制)\n\n 指令码(二进制形式)`00001DCB`\n\n - D=1开显示,D=0关显示\n\n - C=1显示光标,C=0不显示光标\n - B=1光标闪烁,B=0不闪烁\n\n 指令码(二进制形式)`000001NS`\n\n - N=1当读或写一个字符后地址和光标自动+1,N=0则为自动-1;\n\n - S=1当写入一个字符整屏显示左移(N=1)或右移(N=0)达到光标不动屏幕移动的效果;\n\n S=0当写入一个字符,屏幕显示不移动\n\n3. 数据控制\n\n 控制器内部有地址指针,通过发送指令 `80H+地址码` (地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置\n\n4. 其他指令:\n\n ![image-20210825162336247](https://gitee.com/ajream/images/raw/master/img/20210825162337_image-20210825162336247.png)\n\n\n\n\n\n### 时序图\n\n\n\n读操作时序图\n\n\n\n![image-20210825162506774](https://gitee.com/ajream/images/raw/master/img/20210825162508_image-20210825162506774.png)\n\n\n\n写操作时序图\n\n![image-20210825162527365](https://gitee.com/ajream/images/raw/master/img/20210825162528_image-20210825162527365.png)\n\n\n\n时序参数\n\n\n\n![image-20210825162600974](https://gitee.com/ajream/images/raw/master/img/20210825162602_image-20210825162600974.png)\n\n\n\n\n\n## 例子\n\n控制LCD1602显示字符12345\n\n\n\n```c\n#include \n\ntypedef unsigned char uchar;\ntypedef unsigned int uint;\n\nsbit RS = P3^5;\nsbit RW = P3^6;\nsbit EN = P3^4;\n\n//判断液晶忙,如果忙则等待\nvoid Read_Busy()\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n\tRead_Busy();//判断忙\n\tRS = 0;\n\tRW = 0;\n\tP0 = cmd;\n\tEN = 1;\n\tEN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\nvoid main()\n{\n\tWrite_Cmd(0x38);//设置16*2显示\n\tWrite_Cmd(0x0f);//开显示,显示光标,光标闪烁\n\tWrite_Cmd(0x01);//清屏\n\n\tWrite_Cmd(0x06);//地址指针移位命令\n\tWrite_Cmd(0x80 | 0x06);//显示地址\n\n\tWrite_Dat(1 + '0'); //写入的字符要转换位ASCII码\n\tWrite_Dat(2 + '0');\n\tWrite_Dat(3 + '0');\n\tWrite_Dat(4 + '0');\n\tWrite_Dat(5 + '0');\n\twhile(1);\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(九).md","raw":"---\ntitle: 51单片机学习笔记(九)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: LCD1602液晶显示器基本操作使用\nabbrlink: '17528481'\ndate: 2021-08-21 14:32:19\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n## 简单介绍\n\n### 主要参数\n\n显示容量:16X2个字符\n\n芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)\n\n\n\n\n\n### 引脚说明\n\nRS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个\"格子\");\n\n​\t为1时表示数据输入,即每个”格子“显示的内容\n\nR/W:为1时是读,0是写;\n\nE:读写数据时要置1\n\nD0~D7:数据输入输出\n\n![image-20210825154248774](https://gitee.com/ajream/images/raw/master/img/20210825154252_image-20210825154248774.png)\n\n\n\n\n\n## 基本操作\n\n### 读写操作\n\n- 读状态,输入:RS=0, RW=1, E=0; \n\n 此时输出数据D0~D7为状态字,每一位都有各自的状态;\n\n 其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;\n\n ![image-20210825155602088](https://gitee.com/ajream/images/raw/master/img/20210825155603_image-20210825155602088.png)\n\n- 写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码\n- 读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据\n- 写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲\n\n\n\n```c\nvoid Read_Busy() //读状态判断是芯片否处于”忙“状态\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n Read_Busy();//判断忙\n RS = 0;\n RW = 0;\n P0 = cmd;\n EN = 1;\n EN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\n```\n\n\n\n\n\n### RAM地址映射\n\n控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:\n\n![image-20210825160547241](https://gitee.com/ajream/images/raw/master/img/20210825160548_image-20210825160547241.png)\n\n注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符\n\n\n\n### 指令说明\n\n初始化设置:\n\n1. 显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口\n\n2. 开/关光标设置:(配置时要转为16进制)\n\n 指令码(二进制形式)`00001DCB`\n\n - D=1开显示,D=0关显示\n\n - C=1显示光标,C=0不显示光标\n - B=1光标闪烁,B=0不闪烁\n\n 指令码(二进制形式)`000001NS`\n\n - N=1当读或写一个字符后地址和光标自动+1,N=0则为自动-1;\n\n - S=1当写入一个字符整屏显示左移(N=1)或右移(N=0)达到光标不动屏幕移动的效果;\n\n S=0当写入一个字符,屏幕显示不移动\n\n3. 数据控制\n\n 控制器内部有地址指针,通过发送指令 `80H+地址码` (地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置\n\n4. 其他指令:\n\n ![image-20210825162336247](https://gitee.com/ajream/images/raw/master/img/20210825162337_image-20210825162336247.png)\n\n\n\n\n\n### 时序图\n\n\n\n读操作时序图\n\n\n\n![image-20210825162506774](https://gitee.com/ajream/images/raw/master/img/20210825162508_image-20210825162506774.png)\n\n\n\n写操作时序图\n\n![image-20210825162527365](https://gitee.com/ajream/images/raw/master/img/20210825162528_image-20210825162527365.png)\n\n\n\n时序参数\n\n\n\n![image-20210825162600974](https://gitee.com/ajream/images/raw/master/img/20210825162602_image-20210825162600974.png)\n\n\n\n\n\n## 例子\n\n控制LCD1602显示字符12345\n\n\n\n```c\n#include \n\ntypedef unsigned char uchar;\ntypedef unsigned int uint;\n\nsbit RS = P3^5;\nsbit RW = P3^6;\nsbit EN = P3^4;\n\n//判断液晶忙,如果忙则等待\nvoid Read_Busy()\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n\tRead_Busy();//判断忙\n\tRS = 0;\n\tRW = 0;\n\tP0 = cmd;\n\tEN = 1;\n\tEN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\nvoid main()\n{\n\tWrite_Cmd(0x38);//设置16*2显示\n\tWrite_Cmd(0x0f);//开显示,显示光标,光标闪烁\n\tWrite_Cmd(0x01);//清屏\n\n\tWrite_Cmd(0x06);//地址指针移位命令\n\tWrite_Cmd(0x80 | 0x06);//显示地址\n\n\tWrite_Dat(1 + '0'); //写入的字符要转换位ASCII码\n\tWrite_Dat(2 + '0');\n\tWrite_Dat(3 + '0');\n\tWrite_Dat(4 + '0');\n\tWrite_Dat(5 + '0');\n\twhile(1);\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(九)","published":1,"updated":"2021-09-10T12:45:12.950Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o30007akve3mey9pm8","content":"

简单介绍

主要参数

显示容量:16X2个字符

\n

芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)

\n

引脚说明

RS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个”格子”);

\n

​ 为1时表示数据输入,即每个”格子“显示的内容

\n

R/W:为1时是读,0是写;

\n

E:读写数据时要置1

\n

D0~D7:数据输入输出

\n

\"image-20210825154248774\"

\n

基本操作

读写操作

    \n
  • 读状态,输入:RS=0, RW=1, E=0;

    \n

    此时输出数据D0~D7为状态字,每一位都有各自的状态;

    \n

    其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;

    \n

    \"image-20210825155602088\"

    \n
  • \n
  • 写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码

    \n
  • \n
  • 读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据
  • \n
  • 写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void Read_Busy() //读状态判断是芯片否处于”忙“状态
{
\tuchar busy;
\tP0 = 0xff;
\tRS = 0;
\tRW = 1;
\tdo
\t{
\t\tEN = 1;
\t\tbusy = P0;
\t\tEN = 0;
\t}while(busy & 0x80);
}
//写LCD1602命令一个字节
void Write_Cmd(uchar cmd)
{
Read_Busy();//判断忙
RS = 0;
RW = 0;
P0 = cmd;
EN = 1;
EN = 0;
}
//写一个字节数据
void Write_Dat(uchar dat)
{
\tRead_Busy();
\tRS = 1;
\tRW = 0;
\tP0 = dat;
\tEN = 1;
\tEN = 0;
}
\n

RAM地址映射

控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:

\n

\"image-20210825160547241\"

\n

注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符

\n

指令说明

初始化设置:

\n
    \n
  1. 显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口

    \n
  2. \n
  3. 开/关光标设置:(配置时要转为16进制)

    \n

    指令码(二进制形式)00001DCB

    \n
      \n
    • D=1开显示,D=0关显示

      \n
    • \n
    • C=1显示光标,C=0不显示光标

      \n
    • \n
    • B=1光标闪烁,B=0不闪烁
    • \n
    \n

    指令码(二进制形式)000001NS

    \n
      \n
    • N=1当读或写一个字符后地址和光标自动+1,N=0则为自动-1;

      \n
    • \n
    • S=1当写入一个字符整屏显示左移(N=1)或右移(N=0)达到光标不动屏幕移动的效果;

      \n

      S=0当写入一个字符,屏幕显示不移动

      \n
    • \n
    \n
  4. \n
  5. 数据控制

    \n

    控制器内部有地址指针,通过发送指令 80H+地址码 (地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置

    \n
  6. \n
  7. 其他指令:

    \n

    \"image-20210825162336247\"

    \n
  8. \n
\n

时序图

读操作时序图

\n

\"image-20210825162506774\"

\n

写操作时序图

\n

\"image-20210825162527365\"

\n

时序参数

\n

\"image-20210825162600974\"

\n

例子

控制LCD1602显示字符12345

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <reg52.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit RS = P3^5;
sbit RW = P3^6;
sbit EN = P3^4;

//判断液晶忙,如果忙则等待
void Read_Busy()
{
\tuchar busy;
\tP0 = 0xff;
\tRS = 0;
\tRW = 1;
\tdo
\t{
\t\tEN = 1;
\t\tbusy = P0;
\t\tEN = 0;
\t}while(busy & 0x80);
}
//写LCD1602命令一个字节
void Write_Cmd(uchar cmd)
{
\tRead_Busy();//判断忙
\tRS = 0;
\tRW = 0;
\tP0 = cmd;
\tEN = 1;
\tEN = 0;
}
//写一个字节数据
void Write_Dat(uchar dat)
{
\tRead_Busy();
\tRS = 1;
\tRW = 0;
\tP0 = dat;
\tEN = 1;
\tEN = 0;
}
void main()
{
\tWrite_Cmd(0x38);//设置16*2显示
\tWrite_Cmd(0x0f);//开显示,显示光标,光标闪烁
\tWrite_Cmd(0x01);//清屏

\tWrite_Cmd(0x06);//地址指针移位命令
\tWrite_Cmd(0x80 | 0x06);//显示地址

\tWrite_Dat(1 + '0'); //写入的字符要转换位ASCII码
\tWrite_Dat(2 + '0');
\tWrite_Dat(3 + '0');
\tWrite_Dat(4 + '0');
\tWrite_Dat(5 + '0');
\twhile(1);
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

简单介绍

主要参数

显示容量:16X2个字符

\n

芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)

\n

引脚说明

RS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个”格子”);

\n

​ 为1时表示数据输入,即每个”格子“显示的内容

\n

R/W:为1时是读,0是写;

\n

E:读写数据时要置1

\n

D0~D7:数据输入输出

\n

\"image-20210825154248774\"

\n

基本操作

读写操作

    \n
  • 读状态,输入:RS=0, RW=1, E=0;

    \n

    此时输出数据D0~D7为状态字,每一位都有各自的状态;

    \n

    其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;

    \n

    \"image-20210825155602088\"

    \n
  • \n
  • 写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码

    \n
  • \n
  • 读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据
  • \n
  • 写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void Read_Busy() //读状态判断是芯片否处于”忙“状态
{
\tuchar busy;
\tP0 = 0xff;
\tRS = 0;
\tRW = 1;
\tdo
\t{
\t\tEN = 1;
\t\tbusy = P0;
\t\tEN = 0;
\t}while(busy & 0x80);
}
//写LCD1602命令一个字节
void Write_Cmd(uchar cmd)
{
Read_Busy();//判断忙
RS = 0;
RW = 0;
P0 = cmd;
EN = 1;
EN = 0;
}
//写一个字节数据
void Write_Dat(uchar dat)
{
\tRead_Busy();
\tRS = 1;
\tRW = 0;
\tP0 = dat;
\tEN = 1;
\tEN = 0;
}
\n

RAM地址映射

控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:

\n

\"image-20210825160547241\"

\n

注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符

\n

指令说明

初始化设置:

\n
    \n
  1. 显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口

    \n
  2. \n
  3. 开/关光标设置:(配置时要转为16进制)

    \n

    指令码(二进制形式)00001DCB

    \n
      \n
    • D=1开显示,D=0关显示

      \n
    • \n
    • C=1显示光标,C=0不显示光标

      \n
    • \n
    • B=1光标闪烁,B=0不闪烁
    • \n
    \n

    指令码(二进制形式)000001NS

    \n
      \n
    • N=1当读或写一个字符后地址和光标自动+1,N=0则为自动-1;

      \n
    • \n
    • S=1当写入一个字符整屏显示左移(N=1)或右移(N=0)达到光标不动屏幕移动的效果;

      \n

      S=0当写入一个字符,屏幕显示不移动

      \n
    • \n
    \n
  4. \n
  5. 数据控制

    \n

    控制器内部有地址指针,通过发送指令 80H+地址码 (地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置

    \n
  6. \n
  7. 其他指令:

    \n

    \"image-20210825162336247\"

    \n
  8. \n
\n

时序图

读操作时序图

\n

\"image-20210825162506774\"

\n

写操作时序图

\n

\"image-20210825162527365\"

\n

时序参数

\n

\"image-20210825162600974\"

\n

例子

控制LCD1602显示字符12345

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <reg52.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit RS = P3^5;
sbit RW = P3^6;
sbit EN = P3^4;

//判断液晶忙,如果忙则等待
void Read_Busy()
{
\tuchar busy;
\tP0 = 0xff;
\tRS = 0;
\tRW = 1;
\tdo
\t{
\t\tEN = 1;
\t\tbusy = P0;
\t\tEN = 0;
\t}while(busy & 0x80);
}
//写LCD1602命令一个字节
void Write_Cmd(uchar cmd)
{
\tRead_Busy();//判断忙
\tRS = 0;
\tRW = 0;
\tP0 = cmd;
\tEN = 1;
\tEN = 0;
}
//写一个字节数据
void Write_Dat(uchar dat)
{
\tRead_Busy();
\tRS = 1;
\tRW = 0;
\tP0 = dat;
\tEN = 1;
\tEN = 0;
}
void main()
{
\tWrite_Cmd(0x38);//设置16*2显示
\tWrite_Cmd(0x0f);//开显示,显示光标,光标闪烁
\tWrite_Cmd(0x01);//清屏

\tWrite_Cmd(0x06);//地址指针移位命令
\tWrite_Cmd(0x80 | 0x06);//显示地址

\tWrite_Dat(1 + '0'); //写入的字符要转换位ASCII码
\tWrite_Dat(2 + '0');
\tWrite_Dat(3 + '0');
\tWrite_Dat(4 + '0');
\tWrite_Dat(5 + '0');
\twhile(1);
}
\n"},{"title":"51单片机学习笔记(三)","description":"串口通信","abbrlink":"383e39e3","date":"2021-08-19T05:17:20.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n# 串口通信\n\n## 原理简介\n\n通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式\n\n**并行通信**\n\n并行通信通常是将数据字节的各位用多条数据线同时进行传送\n\n特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。\n\n![image-20210822131931271](https://gitee.com/ajream/images/raw/master/img/20210822131937_image-20210822131931271.png)\n\n**串行通信**\n\n串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。\n\n![image-20210822132211661](https://gitee.com/ajream/images/raw/master/img/20210822132216_image-20210822132211661.png)\n\n**同步、异步通信**\n\n串行通信又分 同步、异步两种\n\n同步:接收方与发送方时钟一致\n\n异步:双方使用各自的时钟,但应该尽可能保持一致\n\n**数据传输方向**\n\n单工:一台设备只能收/发\n\n半双工:同一时间只能收或发\n\n全双工:可同时进行收发\n\n![image-20210822134038629](https://gitee.com/ajream/images/raw/master/img/20210822134041_image-20210822134038629.png)\n\n**奇偶校验**\n\n在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。\n\n- 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;\n\n- 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。\n\n**代码和校验**\n\n代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。\n\n**循环冗余校验**\n\n这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。\n\n\n\n## 使用串口进行数据发送和接收\n\n### 初始化配置\n\n1. **打开总中断和串口中断**\n\n ![image-20210822141906412](https://gitee.com/ajream/images/raw/master/img/20210822141909_image-20210822141906412.png)\n2. **选择串口工作方式(配置串口控制寄存器SCON)**不懂就先选方式1\n\n ![image-20210822142218968](https://gitee.com/ajream/images/raw/master/img/20210822142220_image-20210822142218968.png)\n3. **允许串口接收(配置串口控制寄存器SCON)**\n\n ![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n ![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n4. **配置波特率**\n\n 假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD\n\n ![image-20210822143113859](https://gitee.com/ajream/images/raw/master/img/20210822143115_image-20210822143113859.png)\n\n 代码如下:\n\n ```c\n void UARTInit()\n {\n \tEA = 1;\t//打开总中断\n \tES = 1; //打开串口中断\n \tSM0 = 0;\tSM1 = 1; //串口工作方式1,8位UART波特率可变\n \tREN = 1;//串口允许接收\n \n \tTR1 = 1;//启动定时器1\n \tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n \tTH1 = 0xfd;\n \tTL1 = 0xfd;//设置比特率9600\n }\n ```\n\n > 说明:TMOD |= 1, 使用了 `|=` 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式\n >\n > ![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n >\n > \n >\n > 这是T0的初始化:\n >\n > ```c\n > void T0_init()\n > {\n > \tEA = 1;\t//打开总中断\n > \tET0 = 1;//打开定时器0中断\n > \tTR0 = 1;\t //启动定时器0\n > \tREN = 1;//允许串口接收\n > \tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n > \tTH0 = 0xED;\n > \tTL0 = 0xFF; //定时5ms\n > }\n > \n > void UARTInit(){\n > //......\n > }\n > ```\n\n \n\n \n\n### 发送、接收数据\n\n\n\n![image-20210822145358670](https://gitee.com/ajream/images/raw/master/img/20210822145359_image-20210822145358670.png)\n\n接收:通过读取接收缓冲器SBUF中的数据\n\n发送:通过往发送缓冲器SBUF写入数据\n\n发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99\n\nRI、TI看这2幅图,如下\n\n![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n\n```c\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)\t\t\t\t\t//判断接收是否完成\n\t{\n\t\tnum = SBUF;\t\t\t//读SBUF,读出串口接收到的数据\n\t\tRI = 0;\t\t\t\t//软件清零接收标志位\t\n\t\ttemp = num;\n\t\tSBUF = ++temp; \t//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)\t\t\t\t\t//判断是否发送完成\n\t\tTI = 0;\t\t\t\t//清零发送完成标志位\t\n} \n```\n\n\n\n\n\n完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tREN = 1;//允许串口接收\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\n//串口初始化\nvoid UART_init()\n{\n\tEA = 1;\t//打开总中断\n\tES = 1; //打开串口中断\n\tSM0 = 0;\tSM1 = 1;//串口工作方式1,8位UART波特率可变\n\tREN = 1;//串口允许接收\n\tTR1 = 1;//启动定时器1\n\tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n\tTH1 = 0xfd;\n\tTL1 = 0xfd;//设置比特率9600\n}\nvoid main()//main函数自身会循环\n{\t\n\tT0_init();//定时器0初始化\n\tUART_init();//串口初始化\n\twhile(1);\t\n} \n\n//定时器0中断函数\nvoid t0() interrupt 1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n}\n\n//串口中断函数\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)//判断接收是否完成\n\t{\n\t\tnum = SBUF;//读SBUF,读出串口接收到的数据\n\t\tRI = 0;//软件清零接收标志位\t\n\t\ttemp = num;//\n\t\tSBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)//判断是否发送完成\n\t\tTI = 0;//清零发送完成标志位\t\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(三).md","raw":"---\ntitle: 51单片机学习笔记(三)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 串口通信\nabbrlink: 383e39e3\ndate: 2021-08-19 13:17:20\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n# 串口通信\n\n## 原理简介\n\n通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式\n\n**并行通信**\n\n并行通信通常是将数据字节的各位用多条数据线同时进行传送\n\n特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。\n\n![image-20210822131931271](https://gitee.com/ajream/images/raw/master/img/20210822131937_image-20210822131931271.png)\n\n**串行通信**\n\n串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。\n\n![image-20210822132211661](https://gitee.com/ajream/images/raw/master/img/20210822132216_image-20210822132211661.png)\n\n**同步、异步通信**\n\n串行通信又分 同步、异步两种\n\n同步:接收方与发送方时钟一致\n\n异步:双方使用各自的时钟,但应该尽可能保持一致\n\n**数据传输方向**\n\n单工:一台设备只能收/发\n\n半双工:同一时间只能收或发\n\n全双工:可同时进行收发\n\n![image-20210822134038629](https://gitee.com/ajream/images/raw/master/img/20210822134041_image-20210822134038629.png)\n\n**奇偶校验**\n\n在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。\n\n- 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;\n\n- 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。\n\n**代码和校验**\n\n代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。\n\n**循环冗余校验**\n\n这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。\n\n\n\n## 使用串口进行数据发送和接收\n\n### 初始化配置\n\n1. **打开总中断和串口中断**\n\n ![image-20210822141906412](https://gitee.com/ajream/images/raw/master/img/20210822141909_image-20210822141906412.png)\n2. **选择串口工作方式(配置串口控制寄存器SCON)**不懂就先选方式1\n\n ![image-20210822142218968](https://gitee.com/ajream/images/raw/master/img/20210822142220_image-20210822142218968.png)\n3. **允许串口接收(配置串口控制寄存器SCON)**\n\n ![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n ![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n4. **配置波特率**\n\n 假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD\n\n ![image-20210822143113859](https://gitee.com/ajream/images/raw/master/img/20210822143115_image-20210822143113859.png)\n\n 代码如下:\n\n ```c\n void UARTInit()\n {\n \tEA = 1;\t//打开总中断\n \tES = 1; //打开串口中断\n \tSM0 = 0;\tSM1 = 1; //串口工作方式1,8位UART波特率可变\n \tREN = 1;//串口允许接收\n \n \tTR1 = 1;//启动定时器1\n \tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n \tTH1 = 0xfd;\n \tTL1 = 0xfd;//设置比特率9600\n }\n ```\n\n > 说明:TMOD |= 1, 使用了 `|=` 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式\n >\n > ![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n >\n > \n >\n > 这是T0的初始化:\n >\n > ```c\n > void T0_init()\n > {\n > \tEA = 1;\t//打开总中断\n > \tET0 = 1;//打开定时器0中断\n > \tTR0 = 1;\t //启动定时器0\n > \tREN = 1;//允许串口接收\n > \tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n > \tTH0 = 0xED;\n > \tTL0 = 0xFF; //定时5ms\n > }\n > \n > void UARTInit(){\n > //......\n > }\n > ```\n\n \n\n \n\n### 发送、接收数据\n\n\n\n![image-20210822145358670](https://gitee.com/ajream/images/raw/master/img/20210822145359_image-20210822145358670.png)\n\n接收:通过读取接收缓冲器SBUF中的数据\n\n发送:通过往发送缓冲器SBUF写入数据\n\n发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99\n\nRI、TI看这2幅图,如下\n\n![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n\n```c\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)\t\t\t\t\t//判断接收是否完成\n\t{\n\t\tnum = SBUF;\t\t\t//读SBUF,读出串口接收到的数据\n\t\tRI = 0;\t\t\t\t//软件清零接收标志位\t\n\t\ttemp = num;\n\t\tSBUF = ++temp; \t//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)\t\t\t\t\t//判断是否发送完成\n\t\tTI = 0;\t\t\t\t//清零发送完成标志位\t\n} \n```\n\n\n\n\n\n完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tREN = 1;//允许串口接收\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\n//串口初始化\nvoid UART_init()\n{\n\tEA = 1;\t//打开总中断\n\tES = 1; //打开串口中断\n\tSM0 = 0;\tSM1 = 1;//串口工作方式1,8位UART波特率可变\n\tREN = 1;//串口允许接收\n\tTR1 = 1;//启动定时器1\n\tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n\tTH1 = 0xfd;\n\tTL1 = 0xfd;//设置比特率9600\n}\nvoid main()//main函数自身会循环\n{\t\n\tT0_init();//定时器0初始化\n\tUART_init();//串口初始化\n\twhile(1);\t\n} \n\n//定时器0中断函数\nvoid t0() interrupt 1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n}\n\n//串口中断函数\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)//判断接收是否完成\n\t{\n\t\tnum = SBUF;//读SBUF,读出串口接收到的数据\n\t\tRI = 0;//软件清零接收标志位\t\n\t\ttemp = num;//\n\t\tSBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)//判断是否发送完成\n\t\tTI = 0;//清零发送完成标志位\t\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(三)","published":1,"updated":"2021-09-10T12:45:31.265Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o40009akvehm657ia2","content":"

串口通信

原理简介

通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式

\n

并行通信

\n

并行通信通常是将数据字节的各位用多条数据线同时进行传送

\n

特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。

\n

\"image-20210822131931271\"

\n

串行通信

\n

串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。

\n

\"image-20210822132211661\"

\n

同步、异步通信

\n

串行通信又分 同步、异步两种

\n

同步:接收方与发送方时钟一致

\n

异步:双方使用各自的时钟,但应该尽可能保持一致

\n

数据传输方向

\n

单工:一台设备只能收/发

\n

半双工:同一时间只能收或发

\n

全双工:可同时进行收发

\n

\"image-20210822134038629\"

\n

奇偶校验

\n

在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。

\n
    \n
  • 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;

    \n
  • \n
  • 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。

    \n
  • \n
\n

代码和校验

\n

代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。

\n

循环冗余校验

\n

这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。

\n

使用串口进行数据发送和接收

初始化配置

    \n
  1. 打开总中断和串口中断

    \n

    \"image-20210822141906412\"

    \n
  2. \n
  3. 选择串口工作方式(配置串口控制寄存器SCON)不懂就先选方式1

    \n

    \"image-20210822142218968\"

    \n
  4. \n
  5. 允许串口接收(配置串口控制寄存器SCON)

    \n

    \"image-20210822142735027\"

    \n

    \"image-20210822150203890\"

    \n
  6. \n
  7. 配置波特率

    \n

    假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD

    \n

    \"image-20210822143113859\"

    \n

    代码如下:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void UARTInit()
    {
    \tEA = 1;\t//打开总中断
    \tES = 1; //打开串口中断
    \tSM0 = 0;\tSM1 = 1; //串口工作方式1,8位UART波特率可变
    \tREN = 1;//串口允许接收

    \tTR1 = 1;//启动定时器1
    \tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装
    \tTH1 = 0xfd;
    \tTL1 = 0xfd;//设置比特率9600
    }
    \n
    \n

    说明:TMOD |= 1, 使用了 |= 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式

    \n

    \"image-20210822100638172\"

    \n

    这是T0的初始化:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void T0_init()
    {
    \tEA = 1;\t//打开总中断
    \tET0 = 1;//打开定时器0中断
    \tTR0 = 1;\t //启动定时器0
    \tREN = 1;//允许串口接收
    \tTMOD |= 0X01; //定时器工作模式1,16位定时模式
    \tTH0 = 0xED;
    \tTL0 = 0xFF; //定时5ms
    }

    void UARTInit(){
    //......
    }
    \n
    \n
  8. \n
\n

发送、接收数据

\"image-20210822145358670\"

\n

接收:通过读取接收缓冲器SBUF中的数据

\n

发送:通过往发送缓冲器SBUF写入数据

\n

发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99

\n

RI、TI看这2幅图,如下

\n

\"image-20210822142735027\"

\n

\"image-20210822150203890\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
void UART() interrupt 4
{
\tuchar temp;
\tif(RI)\t\t\t\t\t//判断接收是否完成
\t{
\t\tnum = SBUF;\t\t\t//读SBUF,读出串口接收到的数据
\t\tRI = 0;\t\t\t\t//软件清零接收标志位\t
\t\ttemp = num;
\t\tSBUF = ++temp; \t//写SBUF,把要发送的数据送给发送缓存器
\t}
\tif(TI)\t\t\t\t\t//判断是否发送完成
\t\tTI = 0;\t\t\t\t//清零发送完成标志位\t
}
\n

完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
uchar num;//数码管显示的值

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};


void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void T0_init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tREN = 1;//允许串口接收
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}

//串口初始化
void UART_init()
{
\tEA = 1;\t//打开总中断
\tES = 1; //打开串口中断
\tSM0 = 0;\tSM1 = 1;//串口工作方式1,8位UART波特率可变
\tREN = 1;//串口允许接收
\tTR1 = 1;//启动定时器1
\tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装
\tTH1 = 0xfd;
\tTL1 = 0xfd;//设置比特率9600
}
void main()//main函数自身会循环
{\t
\tT0_init();//定时器0初始化
\tUART_init();//串口初始化
\twhile(1);\t
}

//定时器0中断函数
void t0() interrupt 1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}

//串口中断函数
void UART() interrupt 4
{
\tuchar temp;
\tif(RI)//判断接收是否完成
\t{
\t\tnum = SBUF;//读SBUF,读出串口接收到的数据
\t\tRI = 0;//软件清零接收标志位\t
\t\ttemp = num;//
\t\tSBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器
\t}
\tif(TI)//判断是否发送完成
\t\tTI = 0;//清零发送完成标志位\t
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

串口通信

原理简介

通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式

\n

并行通信

\n

并行通信通常是将数据字节的各位用多条数据线同时进行传送

\n

特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。

\n

\"image-20210822131931271\"

\n

串行通信

\n

串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。

\n

\"image-20210822132211661\"

\n

同步、异步通信

\n

串行通信又分 同步、异步两种

\n

同步:接收方与发送方时钟一致

\n

异步:双方使用各自的时钟,但应该尽可能保持一致

\n

数据传输方向

\n

单工:一台设备只能收/发

\n

半双工:同一时间只能收或发

\n

全双工:可同时进行收发

\n

\"image-20210822134038629\"

\n

奇偶校验

\n

在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。

\n
    \n
  • 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;

    \n
  • \n
  • 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。

    \n
  • \n
\n

代码和校验

\n

代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。

\n

循环冗余校验

\n

这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。

\n

使用串口进行数据发送和接收

初始化配置

    \n
  1. 打开总中断和串口中断

    \n

    \"image-20210822141906412\"

    \n
  2. \n
  3. 选择串口工作方式(配置串口控制寄存器SCON)不懂就先选方式1

    \n

    \"image-20210822142218968\"

    \n
  4. \n
  5. 允许串口接收(配置串口控制寄存器SCON)

    \n

    \"image-20210822142735027\"

    \n

    \"image-20210822150203890\"

    \n
  6. \n
  7. 配置波特率

    \n

    假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD

    \n

    \"image-20210822143113859\"

    \n

    代码如下:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void UARTInit()
    {
    \tEA = 1;\t//打开总中断
    \tES = 1; //打开串口中断
    \tSM0 = 0;\tSM1 = 1; //串口工作方式1,8位UART波特率可变
    \tREN = 1;//串口允许接收

    \tTR1 = 1;//启动定时器1
    \tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装
    \tTH1 = 0xfd;
    \tTL1 = 0xfd;//设置比特率9600
    }
    \n
    \n

    说明:TMOD |= 1, 使用了 |= 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式

    \n

    \"image-20210822100638172\"

    \n

    这是T0的初始化:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void T0_init()
    {
    \tEA = 1;\t//打开总中断
    \tET0 = 1;//打开定时器0中断
    \tTR0 = 1;\t //启动定时器0
    \tREN = 1;//允许串口接收
    \tTMOD |= 0X01; //定时器工作模式1,16位定时模式
    \tTH0 = 0xED;
    \tTL0 = 0xFF; //定时5ms
    }

    void UARTInit(){
    //......
    }
    \n
    \n
  8. \n
\n

发送、接收数据

\"image-20210822145358670\"

\n

接收:通过读取接收缓冲器SBUF中的数据

\n

发送:通过往发送缓冲器SBUF写入数据

\n

发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99

\n

RI、TI看这2幅图,如下

\n

\"image-20210822142735027\"

\n

\"image-20210822150203890\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
void UART() interrupt 4
{
\tuchar temp;
\tif(RI)\t\t\t\t\t//判断接收是否完成
\t{
\t\tnum = SBUF;\t\t\t//读SBUF,读出串口接收到的数据
\t\tRI = 0;\t\t\t\t//软件清零接收标志位\t
\t\ttemp = num;
\t\tSBUF = ++temp; \t//写SBUF,把要发送的数据送给发送缓存器
\t}
\tif(TI)\t\t\t\t\t//判断是否发送完成
\t\tTI = 0;\t\t\t\t//清零发送完成标志位\t
}
\n

完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
uchar num;//数码管显示的值

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};


void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void T0_init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tREN = 1;//允许串口接收
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}

//串口初始化
void UART_init()
{
\tEA = 1;\t//打开总中断
\tES = 1; //打开串口中断
\tSM0 = 0;\tSM1 = 1;//串口工作方式1,8位UART波特率可变
\tREN = 1;//串口允许接收
\tTR1 = 1;//启动定时器1
\tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装
\tTH1 = 0xfd;
\tTL1 = 0xfd;//设置比特率9600
}
void main()//main函数自身会循环
{\t
\tT0_init();//定时器0初始化
\tUART_init();//串口初始化
\twhile(1);\t
}

//定时器0中断函数
void t0() interrupt 1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}

//串口中断函数
void UART() interrupt 4
{
\tuchar temp;
\tif(RI)//判断接收是否完成
\t{
\t\tnum = SBUF;//读SBUF,读出串口接收到的数据
\t\tRI = 0;//软件清零接收标志位\t
\t\ttemp = num;//
\t\tSBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器
\t}
\tif(TI)//判断是否发送完成
\t\tTI = 0;//清零发送完成标志位\t
}
\n"},{"title":"51单片机学习笔记(二)","description":"定时-计数器、中断系统","abbrlink":"46cd19c8","date":"2021-08-19T00:57:20.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n# 定时计数器\n\n51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)\n\n这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。\n\n当工作在**定时模式**时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。\n可以算出工作在定时模式时最高单次定时时间:65535*1.085us\n\n当工作在**计数器模式**时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1\n\n\n## 使用定时-计数器\n### (一)启动定时计数器\n\nT0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)\n\n![image-20210822095401344](https://gitee.com/ajream/images/raw/master/img/20210822095407_image-20210822095401344.png)\n\n> 总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)\n\n### (二)设定定时计数器的工作模式\n\n这一步由寄存器TMOD(不可位寻址)控制\n\n![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n\n第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数\n\n第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式\n\nM1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:\n\n| M1 | M0 | 工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位) |\n| ---- | ---- | ------------------------------------------------------------ |\n| 0 | 0 | 13位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$ |\n| 0 | 1 | 16位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535 |\n| 1 | 0 | 8位自动重装载方式,溢出时自动将THx的值装载到TLx里面 |\n| 1 | 1 | 如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出) |\n\n### (三)查询定时计数器是否溢出\n\n上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0\n\n\n\n```c\n/******************************************************************\n* 【程序功能】: \t定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。\t\n*******************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tuchar bai, shi, ge;\n\tbai = i / 100; //236 / 100 = 2\n\tshi = i % 100 / 10;\t//236 % 100 / 10 = 3\n\tge = i % 10;//236 % 10 =6\n\t\n\t//第一位数码管 \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\n//定时器T0初始化\nvoid T0_init()\n{\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时器计数模式\n\tTH0 = 0x4b;\n\tTL0 = 0xfd; //定时50ms\n //0x4bfd即19453,共计数65535-19453+1 = 46083次,耗时46083*1.085us = 50 ms\n}\n\nvoid main()//main函数自身会循环\n{\t\n\tuchar mSec, Sec;//毫秒和秒储存变量\n\tT0_init();//定时器T0初始化\n\twhile(1)\n\t{\n\t\tif(TF0 == 1)//判断是否溢出\n\t\t{\n\t\t\tTF0 = 0;//软件清零溢出标志位\n\t\t\tTH0 = 0x4b;\n\t\t\tTL0 = 0xfd; //定时50ms\n\t\t\tmSec++;//50ms到\n\t\t\tif(mSec == 20)\n\t\t\t{\n\t\t\t\tmSec = 0;\n\t\t\t\tSec++;//1秒时间到\n\t\t\t}\t\t\t\t\t\n\t\t}\n\t\tdisplay(Sec); //数码管显示函数\n\t\tif(Sec > 10)\n\t\t\tSec = 0;//秒清零 \n\t}\t\n} \n```\n\n方式1初值计算方法,假如定时t(单位:us),使用T0:\n\n则:\n\n```\nTH0 = (65535 - t / 1.085) / 256\nTL0 = (65535 - t / 1.085) % 256\n四舍五入取整后转换为16进制\n```\n\n\n\n\n\n\n# 中断系统\n\n主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断\n\n![image-20210822084113315](https://gitee.com/ajream/images/raw/master/img/20210822084120_image-20210822084113315.png)\n\n引起CPU中断的根源,称为**中断源**。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。\n\n51子系列存在5个中断源:\n**外部中断源2个**\n\n1. INT0——由P3.2端口线引入,低电平或下降沿引起。\n\n2. INT1——由P3.3端口线引入,低电平或下降沿引起。\n\n 这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制\n\n**内部中断源3个**\n\n1. T0——定时器/计数器0中断,由T0回零溢出引起。\n2. T1——定时器/计数器1中断,由T1回零溢出引起。\n3. TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。\n \n 这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。\n\n## 使用中断\n\n如何使用中断,有3大步骤:\n\n### (一)允许产生中断\n\n要允许产生中断首先要配置中断允许寄存器IE和XICON\n\n(**可位寻址**表示每一位都已经用变量定义好了,比如第7位是EA)\n\n![image-20210822091845225](https://gitee.com/ajream/images/raw/master/img/20210822091850_image-20210822091845225.png)\n\n> 总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断\n\n### (二)设置什么时候响应中断\n\n什么时候响应中断由控制寄存器TCON控制\n\n![image-20210822092158946](https://gitee.com/ajream/images/raw/master/img/20210822092202_image-20210822092158946.png)\n\nTFx:当计数值溢出时,就会向cpu发出中断请求,然后**自动**进入对应的**中断处理函数**,然后TFx由硬件自动清0\n\n其它IEx, ITx也差不多是这样\n\n### (三)产生中断后你要干什么——中断处理函数\n\n中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:\n\n格式如下:\n\n```c\nvoid 函数名() interrupt 中断入口号 //中断处理函数,加关键字interrupt和入口号0/1/2/3/4\n{\n //中断处理语句\n}\n\n```\n\n\n\n> 中断优先级:\n>\n> 如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念\n>\n> 中断优先级级别越高,就越先被执行\n>\n> ![image-20210822120231820](https://gitee.com/ajream/images/raw/master/img/20210822120238_image-20210822120231820.png)\n\n\n\n```c\n/*\n定时器0, 工作模式1, 16位定时计数\n数码管动态显示0-10,每隔1秒显示一次\n*/\n\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\nuchar mSec, Sec;//毫秒和秒储存变量\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\nvoid main() //主程序执行按键扫描\n{\t\n\tT0_init();//定时器0初始化\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 120)\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t}\t\n} \n\n//定时器0中断处理函数\nvoid timer0() interrupt 1 //T0中断入口为1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示s\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(二).md","raw":"---\ntitle: 51单片机学习笔记(二)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 定时-计数器、中断系统\nabbrlink: 46cd19c8\ndate: 2021-08-19 08:57:20\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n# 定时计数器\n\n51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)\n\n这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。\n\n当工作在**定时模式**时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。\n可以算出工作在定时模式时最高单次定时时间:65535*1.085us\n\n当工作在**计数器模式**时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1\n\n\n## 使用定时-计数器\n### (一)启动定时计数器\n\nT0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)\n\n![image-20210822095401344](https://gitee.com/ajream/images/raw/master/img/20210822095407_image-20210822095401344.png)\n\n> 总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)\n\n### (二)设定定时计数器的工作模式\n\n这一步由寄存器TMOD(不可位寻址)控制\n\n![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n\n第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数\n\n第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式\n\nM1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:\n\n| M1 | M0 | 工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位) |\n| ---- | ---- | ------------------------------------------------------------ |\n| 0 | 0 | 13位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$ |\n| 0 | 1 | 16位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535 |\n| 1 | 0 | 8位自动重装载方式,溢出时自动将THx的值装载到TLx里面 |\n| 1 | 1 | 如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出) |\n\n### (三)查询定时计数器是否溢出\n\n上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0\n\n\n\n```c\n/******************************************************************\n* 【程序功能】: \t定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。\t\n*******************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tuchar bai, shi, ge;\n\tbai = i / 100; //236 / 100 = 2\n\tshi = i % 100 / 10;\t//236 % 100 / 10 = 3\n\tge = i % 10;//236 % 10 =6\n\t\n\t//第一位数码管 \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\n//定时器T0初始化\nvoid T0_init()\n{\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时器计数模式\n\tTH0 = 0x4b;\n\tTL0 = 0xfd; //定时50ms\n //0x4bfd即19453,共计数65535-19453+1 = 46083次,耗时46083*1.085us = 50 ms\n}\n\nvoid main()//main函数自身会循环\n{\t\n\tuchar mSec, Sec;//毫秒和秒储存变量\n\tT0_init();//定时器T0初始化\n\twhile(1)\n\t{\n\t\tif(TF0 == 1)//判断是否溢出\n\t\t{\n\t\t\tTF0 = 0;//软件清零溢出标志位\n\t\t\tTH0 = 0x4b;\n\t\t\tTL0 = 0xfd; //定时50ms\n\t\t\tmSec++;//50ms到\n\t\t\tif(mSec == 20)\n\t\t\t{\n\t\t\t\tmSec = 0;\n\t\t\t\tSec++;//1秒时间到\n\t\t\t}\t\t\t\t\t\n\t\t}\n\t\tdisplay(Sec); //数码管显示函数\n\t\tif(Sec > 10)\n\t\t\tSec = 0;//秒清零 \n\t}\t\n} \n```\n\n方式1初值计算方法,假如定时t(单位:us),使用T0:\n\n则:\n\n```\nTH0 = (65535 - t / 1.085) / 256\nTL0 = (65535 - t / 1.085) % 256\n四舍五入取整后转换为16进制\n```\n\n\n\n\n\n\n# 中断系统\n\n主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断\n\n![image-20210822084113315](https://gitee.com/ajream/images/raw/master/img/20210822084120_image-20210822084113315.png)\n\n引起CPU中断的根源,称为**中断源**。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。\n\n51子系列存在5个中断源:\n**外部中断源2个**\n\n1. INT0——由P3.2端口线引入,低电平或下降沿引起。\n\n2. INT1——由P3.3端口线引入,低电平或下降沿引起。\n\n 这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制\n\n**内部中断源3个**\n\n1. T0——定时器/计数器0中断,由T0回零溢出引起。\n2. T1——定时器/计数器1中断,由T1回零溢出引起。\n3. TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。\n \n 这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。\n\n## 使用中断\n\n如何使用中断,有3大步骤:\n\n### (一)允许产生中断\n\n要允许产生中断首先要配置中断允许寄存器IE和XICON\n\n(**可位寻址**表示每一位都已经用变量定义好了,比如第7位是EA)\n\n![image-20210822091845225](https://gitee.com/ajream/images/raw/master/img/20210822091850_image-20210822091845225.png)\n\n> 总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断\n\n### (二)设置什么时候响应中断\n\n什么时候响应中断由控制寄存器TCON控制\n\n![image-20210822092158946](https://gitee.com/ajream/images/raw/master/img/20210822092202_image-20210822092158946.png)\n\nTFx:当计数值溢出时,就会向cpu发出中断请求,然后**自动**进入对应的**中断处理函数**,然后TFx由硬件自动清0\n\n其它IEx, ITx也差不多是这样\n\n### (三)产生中断后你要干什么——中断处理函数\n\n中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:\n\n格式如下:\n\n```c\nvoid 函数名() interrupt 中断入口号 //中断处理函数,加关键字interrupt和入口号0/1/2/3/4\n{\n //中断处理语句\n}\n\n```\n\n\n\n> 中断优先级:\n>\n> 如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念\n>\n> 中断优先级级别越高,就越先被执行\n>\n> ![image-20210822120231820](https://gitee.com/ajream/images/raw/master/img/20210822120238_image-20210822120231820.png)\n\n\n\n```c\n/*\n定时器0, 工作模式1, 16位定时计数\n数码管动态显示0-10,每隔1秒显示一次\n*/\n\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\nuchar mSec, Sec;//毫秒和秒储存变量\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\nvoid main() //主程序执行按键扫描\n{\t\n\tT0_init();//定时器0初始化\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 120)\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t}\t\n} \n\n//定时器0中断处理函数\nvoid timer0() interrupt 1 //T0中断入口为1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示s\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(二)","published":1,"updated":"2021-09-10T12:45:06.012Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o6000bakve2lmd5swc","content":"

定时计数器

51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)

\n

这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。

\n

当工作在定时模式时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。
可以算出工作在定时模式时最高单次定时时间:65535*1.085us

\n

当工作在计数器模式时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1

\n

使用定时-计数器

(一)启动定时计数器

T0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)

\n

\"image-20210822095401344\"

\n
\n

总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)

\n
\n

(二)设定定时计数器的工作模式

这一步由寄存器TMOD(不可位寻址)控制

\n

\"image-20210822100638172\"

\n

第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数

\n

第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式

\n

M1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
M1M0工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位)
0013位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$
0116位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535
108位自动重装载方式,溢出时自动将THx的值装载到TLx里面
11如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出)
\n
\n

(三)查询定时计数器是否溢出

上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/******************************************************************
* 【程序功能】: \t定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。\t
*******************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void display(uchar i)
{
\tuchar bai, shi, ge;
\tbai = i / 100; //236 / 100 = 2
\tshi = i % 100 / 10;\t//236 % 100 / 10 = 3
\tge = i % 10;//236 % 10 =6
\t
\t//第一位数码管 \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[bai];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第二位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFD; //1111 1101
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[shi];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第三位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFB; //1111 1011
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[ge];//
\tDU = 0;//锁存段选数据
\tdelay(5);
}

//定时器T0初始化
void T0_init()
{
\tTR0 = 1;\t //启动定时器0
\tTMOD = 0X01; //定时器工作模式1,16位定时器计数模式
\tTH0 = 0x4b;
\tTL0 = 0xfd; //定时50ms
//0x4bfd即19453,共计数65535-19453+1 = 46083次,耗时46083*1.085us = 50 ms
}

void main()//main函数自身会循环
{\t
\tuchar mSec, Sec;//毫秒和秒储存变量
\tT0_init();//定时器T0初始化
\twhile(1)
\t{
\t\tif(TF0 == 1)//判断是否溢出
\t\t{
\t\t\tTF0 = 0;//软件清零溢出标志位
\t\t\tTH0 = 0x4b;
\t\t\tTL0 = 0xfd; //定时50ms
\t\t\tmSec++;//50ms到
\t\t\tif(mSec == 20)
\t\t\t{
\t\t\t\tmSec = 0;
\t\t\t\tSec++;//1秒时间到
\t\t\t}\t\t\t\t\t
\t\t}
\t\tdisplay(Sec); //数码管显示函数
\t\tif(Sec > 10)
\t\t\tSec = 0;//秒清零
\t}\t
}
\n

方式1初值计算方法,假如定时t(单位:us),使用T0:

\n

则:

\n
1
2
3
TH0 = (65535 - t / 1.085) / 256
TL0 = (65535 - t / 1.085) % 256
四舍五入取整后转换为16进制
\n

中断系统

主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断

\n

\"image-20210822084113315\"

\n

引起CPU中断的根源,称为中断源。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。

\n

51子系列存在5个中断源:
外部中断源2个

\n
    \n
  1. INT0——由P3.2端口线引入,低电平或下降沿引起。

    \n
  2. \n
  3. INT1——由P3.3端口线引入,低电平或下降沿引起。

    \n

    这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制

    \n
  4. \n
\n

内部中断源3个

\n
    \n
  1. T0——定时器/计数器0中断,由T0回零溢出引起。
  2. \n
  3. T1——定时器/计数器1中断,由T1回零溢出引起。
  4. \n
  5. TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。

    \n

    这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。

    \n
  6. \n
\n

使用中断

如何使用中断,有3大步骤:

\n

(一)允许产生中断

要允许产生中断首先要配置中断允许寄存器IE和XICON

\n

(可位寻址表示每一位都已经用变量定义好了,比如第7位是EA)

\n

\"image-20210822091845225\"

\n
\n

总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断

\n
\n

(二)设置什么时候响应中断

什么时候响应中断由控制寄存器TCON控制

\n

\"image-20210822092158946\"

\n

TFx:当计数值溢出时,就会向cpu发出中断请求,然后自动进入对应的中断处理函数,然后TFx由硬件自动清0

\n

其它IEx, ITx也差不多是这样

\n

(三)产生中断后你要干什么——中断处理函数

中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:

\n

格式如下:

\n
1
2
3
4
5
void 函数名() interrupt 中断入口号  //中断处理函数,加关键字interrupt和入口号0/1/2/3/4
{
//中断处理语句
}

\n
\n

中断优先级:

\n

如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念

\n

中断优先级级别越高,就越先被执行

\n

\"image-20210822120231820\"

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*
定时器0, 工作模式1, 16位定时计数
数码管动态显示0-10,每隔1秒显示一次
*/

#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值
uchar mSec, Sec;//毫秒和秒储存变量

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void T0_init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD = 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}

void main() //主程序执行按键扫描
{\t
\tT0_init();//定时器0初始化
\twhile(1)
\t{
\t\tif(key_s2 == 0)//判断S2是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s2 == 0)
\t\t\t{
\t\t\t\tif(num != 120)
\t\t\t\tnum++;
\t\t\t\twhile(!key_s2);//松手检测
\t\t\t}\t
\t\t}
\t\tif(key_s3 == 0)//判断S3是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s3 == 0)
\t\t\t{
\t\t\t\tif(num > 0)
\t\t\t\t\tnum--;
\t\t\t\twhile(!key_s3);//松手检测
\t\t\t}\t
\t\t}
\t}\t
}

//定时器0中断处理函数
void timer0() interrupt 1 //T0中断入口为1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示s
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

定时计数器

51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)

\n

这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。

\n

当工作在定时模式时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。
可以算出工作在定时模式时最高单次定时时间:65535*1.085us

\n

当工作在计数器模式时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1

\n

使用定时-计数器

(一)启动定时计数器

T0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)

\n

\"image-20210822095401344\"

\n
\n

总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)

\n
\n

(二)设定定时计数器的工作模式

这一步由寄存器TMOD(不可位寻址)控制

\n

\"image-20210822100638172\"

\n

第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数

\n

第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式

\n

M1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
M1M0工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位)
0013位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$
0116位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535
108位自动重装载方式,溢出时自动将THx的值装载到TLx里面
11如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出)
\n
\n

(三)查询定时计数器是否溢出

上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/******************************************************************
* 【程序功能】: \t定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。\t
*******************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void display(uchar i)
{
\tuchar bai, shi, ge;
\tbai = i / 100; //236 / 100 = 2
\tshi = i % 100 / 10;\t//236 % 100 / 10 = 3
\tge = i % 10;//236 % 10 =6
\t
\t//第一位数码管 \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[bai];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第二位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFD; //1111 1101
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[shi];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第三位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFB; //1111 1011
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[ge];//
\tDU = 0;//锁存段选数据
\tdelay(5);
}

//定时器T0初始化
void T0_init()
{
\tTR0 = 1;\t //启动定时器0
\tTMOD = 0X01; //定时器工作模式1,16位定时器计数模式
\tTH0 = 0x4b;
\tTL0 = 0xfd; //定时50ms
//0x4bfd即19453,共计数65535-19453+1 = 46083次,耗时46083*1.085us = 50 ms
}

void main()//main函数自身会循环
{\t
\tuchar mSec, Sec;//毫秒和秒储存变量
\tT0_init();//定时器T0初始化
\twhile(1)
\t{
\t\tif(TF0 == 1)//判断是否溢出
\t\t{
\t\t\tTF0 = 0;//软件清零溢出标志位
\t\t\tTH0 = 0x4b;
\t\t\tTL0 = 0xfd; //定时50ms
\t\t\tmSec++;//50ms到
\t\t\tif(mSec == 20)
\t\t\t{
\t\t\t\tmSec = 0;
\t\t\t\tSec++;//1秒时间到
\t\t\t}\t\t\t\t\t
\t\t}
\t\tdisplay(Sec); //数码管显示函数
\t\tif(Sec > 10)
\t\t\tSec = 0;//秒清零
\t}\t
}
\n

方式1初值计算方法,假如定时t(单位:us),使用T0:

\n

则:

\n
1
2
3
TH0 = (65535 - t / 1.085) / 256
TL0 = (65535 - t / 1.085) % 256
四舍五入取整后转换为16进制
\n

中断系统

主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断

\n

\"image-20210822084113315\"

\n

引起CPU中断的根源,称为中断源。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。

\n

51子系列存在5个中断源:
外部中断源2个

\n
    \n
  1. INT0——由P3.2端口线引入,低电平或下降沿引起。

    \n
  2. \n
  3. INT1——由P3.3端口线引入,低电平或下降沿引起。

    \n

    这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制

    \n
  4. \n
\n

内部中断源3个

\n
    \n
  1. T0——定时器/计数器0中断,由T0回零溢出引起。
  2. \n
  3. T1——定时器/计数器1中断,由T1回零溢出引起。
  4. \n
  5. TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。

    \n

    这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。

    \n
  6. \n
\n

使用中断

如何使用中断,有3大步骤:

\n

(一)允许产生中断

要允许产生中断首先要配置中断允许寄存器IE和XICON

\n

(可位寻址表示每一位都已经用变量定义好了,比如第7位是EA)

\n

\"image-20210822091845225\"

\n
\n

总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断

\n
\n

(二)设置什么时候响应中断

什么时候响应中断由控制寄存器TCON控制

\n

\"image-20210822092158946\"

\n

TFx:当计数值溢出时,就会向cpu发出中断请求,然后自动进入对应的中断处理函数,然后TFx由硬件自动清0

\n

其它IEx, ITx也差不多是这样

\n

(三)产生中断后你要干什么——中断处理函数

中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:

\n

格式如下:

\n
1
2
3
4
5
void 函数名() interrupt 中断入口号  //中断处理函数,加关键字interrupt和入口号0/1/2/3/4
{
//中断处理语句
}

\n
\n

中断优先级:

\n

如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念

\n

中断优先级级别越高,就越先被执行

\n

\"image-20210822120231820\"

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*
定时器0, 工作模式1, 16位定时计数
数码管动态显示0-10,每隔1秒显示一次
*/

#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值
uchar mSec, Sec;//毫秒和秒储存变量

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void T0_init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD = 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}

void main() //主程序执行按键扫描
{\t
\tT0_init();//定时器0初始化
\twhile(1)
\t{
\t\tif(key_s2 == 0)//判断S2是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s2 == 0)
\t\t\t{
\t\t\t\tif(num != 120)
\t\t\t\tnum++;
\t\t\t\twhile(!key_s2);//松手检测
\t\t\t}\t
\t\t}
\t\tif(key_s3 == 0)//判断S3是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s3 == 0)
\t\t\t{
\t\t\t\tif(num > 0)
\t\t\t\t\tnum--;
\t\t\t\twhile(!key_s3);//松手检测
\t\t\t}\t
\t\t}
\t}\t
}

//定时器0中断处理函数
void timer0() interrupt 1 //T0中断入口为1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示s
}
\n"},{"title":"51单片机学习笔记(八)","description":"DS1302日历时钟芯片","abbrlink":"b836e65","date":"2021-08-20T09:32:44.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n## DS1302\n\n### 芯片功能\n\nDS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。\n\nDS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便\n\n工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。\n\nDS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。\n\n\n\n### 引脚\n\n\n\n![image-20210824170629438](https://gitee.com/ajream/images/raw/master/img/20210824170630_image-20210824170629438.png)\n\n\n\n- VCC1:主电源\n\n- VCC2:备用电源\n\n 当VCC2大于VCC1+0.2V时,由VCC2向DS1302供电否则由VCC1向DS1302供电\n\n- SCLK:串行时钟输入端,控制数据输入与输出\n\n- I/O :双向输入线\n\n- CE:使能端,CE为高时允许读写DS1302数据,为低时禁止读写\n\n\n\n\n\n### 应用电路\n\n开发板实际电路如下:\n\n![image-20210824170855223](https://gitee.com/ajream/images/raw/master/img/20210824170856_image-20210824170855223.png)\n\n说明:\n\nDS1302和单片机的IO连接只需3条线:\n\n- CE数据传输使能端、SCLK串行时钟输入端、I/O串行数据端\n\n (开发板上把这3个引脚分别接到了j5排针上同时通过4.7K电阻上拉到VCC,我们实际使用DS1302芯片时需要用杜邦线把j5和相应的单片机IO链接上)\n\n此外\n\n- X1、X2晶振引脚外接32.768KHZ圆形晶振,给时钟芯片提供工作频率\n\n- VCC2接的开发上的系统电源,VCC1接的预留电池座\n\n- DS1302第4脚接的系统GND\n\n\n\n### DS1302的寄存器\n\n\n\n\n\n![image-20210824174037791](https://gitee.com/ajream/images/raw/master/img/20210824174040_image-20210824174037791.png)\n\n\n\n如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 `0001 0000`)\n\n\n\n- 秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。\n\n- 小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。\n\n- 写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。\n\n \n\n使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令`81h`,读取分,用 `83h`, ......\n\n\n\n\n\n\n\n突发模式:\n\n突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。\n\n突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:\n\n![image-20210824174700324](https://gitee.com/ajream/images/raw/master/img/20210824174701_image-20210824174700324.png)\n\n也就是说,如果发送`BFh`指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间\n\n\n\n\n\n### DS1302时序\n\n\n\nDS1302读写数据时序,数据的传输是从最低位开始(BIT0)。\n\n数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。\n\n\n\n**读数据:**CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。\n\n![image-20210824175336808](https://gitee.com/ajream/images/raw/master/img/20210824175338_image-20210824175336808.png)\n\n\n\n**写数据:**CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。\n\n![image-20210824175358215](https://gitee.com/ajream/images/raw/master/img/20210824175359_image-20210824175358215.png)\n\n\n\n\n\n\n\n## 例子\n\n数码管显示DS1302时钟\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit TSCLK = P1^0;//时钟线 接到P10上用杜邦线\nsbit TIO = P1^1;//数据线,接到P11上\nsbit TRST = P1^2;//使能端,接到P12上\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n\nvoid Display(uchar Hour, Min, Sec)\n{\t\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Hour/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[0];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Hour%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Min/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[2];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Min%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[3];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Sec/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[4];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Sec%10]; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[5];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n\n}\n\n//写DS1302数据\nvoid Write_DS1302_DAT(uchar cmd, uchar dat)\n{\n\tuchar i;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0;//拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = dat & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tdat >>= 1;\t\t //右移一位\n\t}\n}\n//读DS1302数据\nuchar Read_DS1302_DAT(uchar cmd)\n{\n\tuchar i, dat;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0; //拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01;//写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次读1位,读8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线,产生下降沿,DS1302把数据放到TIO上\n\t\tdat >>= 1;\t\t //右移一位\n\t\tif(TIO)\tdat |= 0x80;//读取数据,从最低位开始\n\t\tTSCLK = 1;\t\t\t//拉高时钟总线,以备下一次产生下降沿\n\t}\n\treturn dat;\t//返回读出数据\n}\n\n//数据转BCD码\nuchar Dat_Chg_BCD(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 10;\n\tdat2 = dat % 10;\n\tdat2 = dat2 + dat1 * 16;\n\treturn dat2;\n}\n\n//BCD码转换为数据\nuchar BCD_Chg_Dat(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 16;\n\tdat2 = dat % 16;\n\tdat2 = dat2 + dat1 * 10;\n\treturn dat2;\n}\n\nvoid main()\n{\n\tuchar i;\n\tuchar Sec, Min, Hour;\n\tWrite_DS1302_DAT(0x8e, 0);//清除写保护\n\tWrite_DS1302_DAT(0x80, Dat_Chg_BCD(30));//30秒(并且进行BCD码转换)\n\tWrite_DS1302_DAT(0x82, Dat_Chg_BCD(15));//15分\n\tWrite_DS1302_DAT(0x84, Dat_Chg_BCD(19));//19时\n\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\twhile(1)\n\t{\n\t\tWrite_DS1302_DAT(0x8e, 0); //清除写保护\n\t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));//读秒寄存器(并且进行BCD码转换)\n\t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83));//读分寄存器\n\t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));//读时寄存器\n\t\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\t\tfor(i = 0; i < 50; i++)\t//循环显示时钟\n\t\t\tDisplay(Hour, Min, Sec);\n\n\t}\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(八).md","raw":"---\ntitle: 51单片机学习笔记(八)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: DS1302日历时钟芯片\nabbrlink: b836e65\ndate: 2021-08-20 17:32:44\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n## DS1302\n\n### 芯片功能\n\nDS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。\n\nDS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便\n\n工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。\n\nDS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。\n\n\n\n### 引脚\n\n\n\n![image-20210824170629438](https://gitee.com/ajream/images/raw/master/img/20210824170630_image-20210824170629438.png)\n\n\n\n- VCC1:主电源\n\n- VCC2:备用电源\n\n 当VCC2大于VCC1+0.2V时,由VCC2向DS1302供电否则由VCC1向DS1302供电\n\n- SCLK:串行时钟输入端,控制数据输入与输出\n\n- I/O :双向输入线\n\n- CE:使能端,CE为高时允许读写DS1302数据,为低时禁止读写\n\n\n\n\n\n### 应用电路\n\n开发板实际电路如下:\n\n![image-20210824170855223](https://gitee.com/ajream/images/raw/master/img/20210824170856_image-20210824170855223.png)\n\n说明:\n\nDS1302和单片机的IO连接只需3条线:\n\n- CE数据传输使能端、SCLK串行时钟输入端、I/O串行数据端\n\n (开发板上把这3个引脚分别接到了j5排针上同时通过4.7K电阻上拉到VCC,我们实际使用DS1302芯片时需要用杜邦线把j5和相应的单片机IO链接上)\n\n此外\n\n- X1、X2晶振引脚外接32.768KHZ圆形晶振,给时钟芯片提供工作频率\n\n- VCC2接的开发上的系统电源,VCC1接的预留电池座\n\n- DS1302第4脚接的系统GND\n\n\n\n### DS1302的寄存器\n\n\n\n\n\n![image-20210824174037791](https://gitee.com/ajream/images/raw/master/img/20210824174040_image-20210824174037791.png)\n\n\n\n如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 `0001 0000`)\n\n\n\n- 秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。\n\n- 小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。\n\n- 写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。\n\n \n\n使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令`81h`,读取分,用 `83h`, ......\n\n\n\n\n\n\n\n突发模式:\n\n突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。\n\n突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:\n\n![image-20210824174700324](https://gitee.com/ajream/images/raw/master/img/20210824174701_image-20210824174700324.png)\n\n也就是说,如果发送`BFh`指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间\n\n\n\n\n\n### DS1302时序\n\n\n\nDS1302读写数据时序,数据的传输是从最低位开始(BIT0)。\n\n数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。\n\n\n\n**读数据:**CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。\n\n![image-20210824175336808](https://gitee.com/ajream/images/raw/master/img/20210824175338_image-20210824175336808.png)\n\n\n\n**写数据:**CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。\n\n![image-20210824175358215](https://gitee.com/ajream/images/raw/master/img/20210824175359_image-20210824175358215.png)\n\n\n\n\n\n\n\n## 例子\n\n数码管显示DS1302时钟\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit TSCLK = P1^0;//时钟线 接到P10上用杜邦线\nsbit TIO = P1^1;//数据线,接到P11上\nsbit TRST = P1^2;//使能端,接到P12上\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n\nvoid Display(uchar Hour, Min, Sec)\n{\t\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Hour/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[0];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Hour%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Min/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[2];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Min%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[3];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Sec/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[4];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Sec%10]; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[5];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n\n}\n\n//写DS1302数据\nvoid Write_DS1302_DAT(uchar cmd, uchar dat)\n{\n\tuchar i;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0;//拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = dat & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tdat >>= 1;\t\t //右移一位\n\t}\n}\n//读DS1302数据\nuchar Read_DS1302_DAT(uchar cmd)\n{\n\tuchar i, dat;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0; //拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01;//写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次读1位,读8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线,产生下降沿,DS1302把数据放到TIO上\n\t\tdat >>= 1;\t\t //右移一位\n\t\tif(TIO)\tdat |= 0x80;//读取数据,从最低位开始\n\t\tTSCLK = 1;\t\t\t//拉高时钟总线,以备下一次产生下降沿\n\t}\n\treturn dat;\t//返回读出数据\n}\n\n//数据转BCD码\nuchar Dat_Chg_BCD(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 10;\n\tdat2 = dat % 10;\n\tdat2 = dat2 + dat1 * 16;\n\treturn dat2;\n}\n\n//BCD码转换为数据\nuchar BCD_Chg_Dat(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 16;\n\tdat2 = dat % 16;\n\tdat2 = dat2 + dat1 * 10;\n\treturn dat2;\n}\n\nvoid main()\n{\n\tuchar i;\n\tuchar Sec, Min, Hour;\n\tWrite_DS1302_DAT(0x8e, 0);//清除写保护\n\tWrite_DS1302_DAT(0x80, Dat_Chg_BCD(30));//30秒(并且进行BCD码转换)\n\tWrite_DS1302_DAT(0x82, Dat_Chg_BCD(15));//15分\n\tWrite_DS1302_DAT(0x84, Dat_Chg_BCD(19));//19时\n\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\twhile(1)\n\t{\n\t\tWrite_DS1302_DAT(0x8e, 0); //清除写保护\n\t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));//读秒寄存器(并且进行BCD码转换)\n\t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83));//读分寄存器\n\t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));//读时寄存器\n\t\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\t\tfor(i = 0; i < 50; i++)\t//循环显示时钟\n\t\t\tDisplay(Hour, Min, Sec);\n\n\t}\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(八)","published":1,"updated":"2021-09-10T12:44:55.652Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o9000gakve5fnphajo","content":"

DS1302

芯片功能

DS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。

\n

DS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便

\n

工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。

\n

DS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。

\n

引脚

\"image-20210824170629438\"

\n
    \n
  • VCC1:主电源

    \n
  • \n
  • VCC2:备用电源

    \n

    当VCC2大于VCC1+0.2V时,由VCC2向DS1302供电否则由VCC1向DS1302供电

    \n
  • \n
  • SCLK:串行时钟输入端,控制数据输入与输出

    \n
  • \n
  • I/O :双向输入线

    \n
  • \n
  • CE:使能端,CE为高时允许读写DS1302数据,为低时禁止读写

    \n
  • \n
\n

应用电路

开发板实际电路如下:

\n

\"image-20210824170855223\"

\n

说明:

\n

DS1302和单片机的IO连接只需3条线:

\n
    \n
  • CE数据传输使能端、SCLK串行时钟输入端、I/O串行数据端

    \n

    (开发板上把这3个引脚分别接到了j5排针上同时通过4.7K电阻上拉到VCC,我们实际使用DS1302芯片时需要用杜邦线把j5和相应的单片机IO链接上)

    \n
  • \n
\n

此外

\n
    \n
  • X1、X2晶振引脚外接32.768KHZ圆形晶振,给时钟芯片提供工作频率

    \n
  • \n
  • VCC2接的开发上的系统电源,VCC1接的预留电池座

    \n
  • \n
  • DS1302第4脚接的系统GND

    \n
  • \n
\n

DS1302的寄存器

\"image-20210824174037791\"

\n

如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 0001 0000

\n
    \n
  • 秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。

    \n
  • \n
  • 小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。

    \n
  • \n
  • 写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。

    \n
  • \n
\n

使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令81h,读取分,用 83h, ……

\n

突发模式:

\n

突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。

\n

突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:

\n

\"image-20210824174700324\"

\n

也就是说,如果发送BFh指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间

\n

DS1302时序

DS1302读写数据时序,数据的传输是从最低位开始(BIT0)。

\n

数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。

\n

读数据:CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。

\n

\"image-20210824175336808\"

\n

写数据:CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。

\n

\"image-20210824175358215\"

\n

例子

数码管显示DS1302时钟

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <reg52.h>
#include <intrins.h>
#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit TSCLK = P1^0;//时钟线 接到P10上用杜邦线
sbit TIO = P1^1;//数据线,接到P11上
sbit TRST = P1^2;//使能端,接到P12上
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
/*====================================
共阴极数码管段选码
====================================*/
uchar code table[]={
//0\t\t1\t 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
};

/*====================================
数码管位选码
====================================*/
\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数:void Delay_Ms(INT16U ms)
参数:ms,毫秒延时形参
描述:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void Delay_Ms(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}

void Display(uchar Hour, Min, Sec)
{\t\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Hour/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[0];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Hour%10]|0x80;
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[1];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Min/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[2];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Min%10]|0x80;
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[3];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Sec/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[4];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Sec%10];
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[5];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);

}

//写DS1302数据
void Write_DS1302_DAT(uchar cmd, uchar dat)
{
\tuchar i;
\tTRST = 0; //拉低使能端
\tTSCLK = 0;//拉低数据总线
\tTRST = 1; //拉高使能端,产生上升沿开始写数据
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = cmd & 0x01; //写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tcmd >>=1;\t\t //右移一位
\t}
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = dat & 0x01; //写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tdat >>= 1;\t\t //右移一位
\t}
}
//读DS1302数据
uchar Read_DS1302_DAT(uchar cmd)
{
\tuchar i, dat;
\tTRST = 0; //拉低使能端
\tTSCLK = 0; //拉低数据总线
\tTRST = 1; //拉高使能端,产生上升沿开始写数据
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = cmd & 0x01;//写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tcmd >>=1;\t\t //右移一位
\t}
\tfor(i = 0; i < 8; i++)//每次读1位,读8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线,产生下降沿,DS1302把数据放到TIO上
\t\tdat >>= 1;\t\t //右移一位
\t\tif(TIO)\tdat |= 0x80;//读取数据,从最低位开始
\t\tTSCLK = 1;\t\t\t//拉高时钟总线,以备下一次产生下降沿
\t}
\treturn dat;\t//返回读出数据
}

//数据转BCD码
uchar Dat_Chg_BCD(uchar dat)
{
\tuchar dat1, dat2;
\tdat1 = dat / 10;
\tdat2 = dat % 10;
\tdat2 = dat2 + dat1 * 16;
\treturn dat2;
}

//BCD码转换为数据
uchar BCD_Chg_Dat(uchar dat)
{
\tuchar dat1, dat2;
\tdat1 = dat / 16;
\tdat2 = dat % 16;
\tdat2 = dat2 + dat1 * 10;
\treturn dat2;
}

void main()
{
\tuchar i;
\tuchar Sec, Min, Hour;
\tWrite_DS1302_DAT(0x8e, 0);//清除写保护
\tWrite_DS1302_DAT(0x80, Dat_Chg_BCD(30));//30秒(并且进行BCD码转换)
\tWrite_DS1302_DAT(0x82, Dat_Chg_BCD(15));//15分
\tWrite_DS1302_DAT(0x84, Dat_Chg_BCD(19));//19时
\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护
\twhile(1)
\t{
\t\tWrite_DS1302_DAT(0x8e, 0); //清除写保护
\t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));//读秒寄存器(并且进行BCD码转换)
\t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83));//读分寄存器
\t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));//读时寄存器
\t\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护
\t\tfor(i = 0; i < 50; i++)\t//循环显示时钟
\t\t\tDisplay(Hour, Min, Sec);

\t}
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

DS1302

芯片功能

DS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。

\n

DS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便

\n

工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。

\n

DS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。

\n

引脚

\"image-20210824170629438\"

\n
    \n
  • VCC1:主电源

    \n
  • \n
  • VCC2:备用电源

    \n

    当VCC2大于VCC1+0.2V时,由VCC2向DS1302供电否则由VCC1向DS1302供电

    \n
  • \n
  • SCLK:串行时钟输入端,控制数据输入与输出

    \n
  • \n
  • I/O :双向输入线

    \n
  • \n
  • CE:使能端,CE为高时允许读写DS1302数据,为低时禁止读写

    \n
  • \n
\n

应用电路

开发板实际电路如下:

\n

\"image-20210824170855223\"

\n

说明:

\n

DS1302和单片机的IO连接只需3条线:

\n
    \n
  • CE数据传输使能端、SCLK串行时钟输入端、I/O串行数据端

    \n

    (开发板上把这3个引脚分别接到了j5排针上同时通过4.7K电阻上拉到VCC,我们实际使用DS1302芯片时需要用杜邦线把j5和相应的单片机IO链接上)

    \n
  • \n
\n

此外

\n
    \n
  • X1、X2晶振引脚外接32.768KHZ圆形晶振,给时钟芯片提供工作频率

    \n
  • \n
  • VCC2接的开发上的系统电源,VCC1接的预留电池座

    \n
  • \n
  • DS1302第4脚接的系统GND

    \n
  • \n
\n

DS1302的寄存器

\"image-20210824174037791\"

\n

如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 0001 0000

\n
    \n
  • 秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。

    \n
  • \n
  • 小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。

    \n
  • \n
  • 写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。

    \n
  • \n
\n

使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令81h,读取分,用 83h, ……

\n

突发模式:

\n

突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。

\n

突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:

\n

\"image-20210824174700324\"

\n

也就是说,如果发送BFh指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间

\n

DS1302时序

DS1302读写数据时序,数据的传输是从最低位开始(BIT0)。

\n

数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。

\n

读数据:CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。

\n

\"image-20210824175336808\"

\n

写数据:CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。

\n

\"image-20210824175358215\"

\n

例子

数码管显示DS1302时钟

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <reg52.h>
#include <intrins.h>
#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit TSCLK = P1^0;//时钟线 接到P10上用杜邦线
sbit TIO = P1^1;//数据线,接到P11上
sbit TRST = P1^2;//使能端,接到P12上
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
/*====================================
共阴极数码管段选码
====================================*/
uchar code table[]={
//0\t\t1\t 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
};

/*====================================
数码管位选码
====================================*/
\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数:void Delay_Ms(INT16U ms)
参数:ms,毫秒延时形参
描述:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void Delay_Ms(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}

void Display(uchar Hour, Min, Sec)
{\t\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Hour/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[0];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Hour%10]|0x80;
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[1];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Min/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[2];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Min%10]|0x80;
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[3];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Sec/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[4];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Sec%10];
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[5];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);

}

//写DS1302数据
void Write_DS1302_DAT(uchar cmd, uchar dat)
{
\tuchar i;
\tTRST = 0; //拉低使能端
\tTSCLK = 0;//拉低数据总线
\tTRST = 1; //拉高使能端,产生上升沿开始写数据
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = cmd & 0x01; //写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tcmd >>=1;\t\t //右移一位
\t}
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = dat & 0x01; //写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tdat >>= 1;\t\t //右移一位
\t}
}
//读DS1302数据
uchar Read_DS1302_DAT(uchar cmd)
{
\tuchar i, dat;
\tTRST = 0; //拉低使能端
\tTSCLK = 0; //拉低数据总线
\tTRST = 1; //拉高使能端,产生上升沿开始写数据
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = cmd & 0x01;//写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tcmd >>=1;\t\t //右移一位
\t}
\tfor(i = 0; i < 8; i++)//每次读1位,读8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线,产生下降沿,DS1302把数据放到TIO上
\t\tdat >>= 1;\t\t //右移一位
\t\tif(TIO)\tdat |= 0x80;//读取数据,从最低位开始
\t\tTSCLK = 1;\t\t\t//拉高时钟总线,以备下一次产生下降沿
\t}
\treturn dat;\t//返回读出数据
}

//数据转BCD码
uchar Dat_Chg_BCD(uchar dat)
{
\tuchar dat1, dat2;
\tdat1 = dat / 10;
\tdat2 = dat % 10;
\tdat2 = dat2 + dat1 * 16;
\treturn dat2;
}

//BCD码转换为数据
uchar BCD_Chg_Dat(uchar dat)
{
\tuchar dat1, dat2;
\tdat1 = dat / 16;
\tdat2 = dat % 16;
\tdat2 = dat2 + dat1 * 10;
\treturn dat2;
}

void main()
{
\tuchar i;
\tuchar Sec, Min, Hour;
\tWrite_DS1302_DAT(0x8e, 0);//清除写保护
\tWrite_DS1302_DAT(0x80, Dat_Chg_BCD(30));//30秒(并且进行BCD码转换)
\tWrite_DS1302_DAT(0x82, Dat_Chg_BCD(15));//15分
\tWrite_DS1302_DAT(0x84, Dat_Chg_BCD(19));//19时
\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护
\twhile(1)
\t{
\t\tWrite_DS1302_DAT(0x8e, 0); //清除写保护
\t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));//读秒寄存器(并且进行BCD码转换)
\t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83));//读分寄存器
\t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));//读时寄存器
\t\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护
\t\tfor(i = 0; i < 50; i++)\t//循环显示时钟
\t\t\tDisplay(Hour, Min, Sec);

\t}
}
\n"},{"title":"Hexo添加百度统计分析","description":"Hexo添加百度统计分析","abbrlink":"c8eb4af9","date":"2021-08-29T02:20:29.000Z","swiper_index":6,"cover":"https://gitee.com/ajream/images/raw/master/img/20210905193431_butterfly.png","_content":"\n\n1. 注册登录\n登录百度统计的[官方网站](https://tongji.baidu.com/web/welcome/login)\n\n2. 添加网站,填写相关信息\n\n ![image-20210830205228402](https://gitee.com/ajream/images/raw/master/img/20210830205231_image-20210830205228402.png)\n\n3. 获取代码![image-20210830205615284](https://gitee.com/ajream/images/raw/master/img/20210830205616_image-20210830205615284.png)\n\n 将上面这串字符填在主题的配置文件`_config.butterfly.yml`中:\n\n ```yml\n # Baidu Analytics\n # https://tongji.baidu.com/web/welcome/login\n baidu_analytics: #填在这里\n \n ```\n\n \n\n","source":"_posts/Hexo/Hexo添加百度统计分析.md","raw":"---\ntitle: Hexo添加百度统计分析\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: Hexo添加百度统计分析\nabbrlink: c8eb4af9\ndate: 2021-08-29 10:20:29\nswiper_index: 6\ncover: https://gitee.com/ajream/images/raw/master/img/20210905193431_butterfly.png\n---\n\n\n1. 注册登录\n登录百度统计的[官方网站](https://tongji.baidu.com/web/welcome/login)\n\n2. 添加网站,填写相关信息\n\n ![image-20210830205228402](https://gitee.com/ajream/images/raw/master/img/20210830205231_image-20210830205228402.png)\n\n3. 获取代码![image-20210830205615284](https://gitee.com/ajream/images/raw/master/img/20210830205616_image-20210830205615284.png)\n\n 将上面这串字符填在主题的配置文件`_config.butterfly.yml`中:\n\n ```yml\n # Baidu Analytics\n # https://tongji.baidu.com/web/welcome/login\n baidu_analytics: #填在这里\n \n ```\n\n \n\n","slug":"Hexo/Hexo添加百度统计分析","published":1,"updated":"2021-09-05T11:34:58.490Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ob000jakve6uur2o8z","content":"
    \n
  1. 注册登录
    登录百度统计的官方网站

    \n
  2. \n
  3. 添加网站,填写相关信息

    \n

    \"image-20210830205228402\"

    \n
  4. \n
  5. 获取代码\"image-20210830205615284\"

    \n

    将上面这串字符填在主题的配置文件_config.butterfly.yml中:

    \n
    1
    2
    3
    4
    # Baidu Analytics
    # https://tongji.baidu.com/web/welcome/login
    baidu_analytics: #填在这里

    \n
  6. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
    \n
  1. 注册登录
    登录百度统计的官方网站

    \n
  2. \n
  3. 添加网站,填写相关信息

    \n

    \"image-20210830205228402\"

    \n
  4. \n
  5. 获取代码\"image-20210830205615284\"

    \n

    将上面这串字符填在主题的配置文件_config.butterfly.yml中:

    \n
    1
    2
    3
    4
    # Baidu Analytics
    # https://tongji.baidu.com/web/welcome/login
    baidu_analytics: #填在这里

    \n
  6. \n
\n"},{"title":"51单片机学习笔记(六)","description":"DS18B20温度传感器介绍","abbrlink":"5dd9c9e3","date":"2021-08-20T07:02:48.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n\n\n## DS18B20介绍\n\nDS18B20数字温度传感器是DALLAS公司生产的单总线器件,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。\n\n\n\n### 特点\n\n1. 通信采用1-Wire接口\n2. n每个DS18B20都有唯一的64位序列码储存在板载ROM中\n3. 无需外部元件\n4. 可从数据线供电,电源范围为3.0V ~ 5.5V。\n5. 可测量的温度范围在-55℃ ~ +125℃\n6. 在-10~+85℃范围内精确度为±0.5℃\n7. 温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃\n\n\n\n\n\n![image-20210824153229536](https://gitee.com/ajream/images/raw/master/img/20210824153232_image-20210824153229536.png)\n\n\n\n## 单总线\n\nDS18B20采用`1-wire Bus`传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。\n\n单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。\n\n所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。\n\n\n\n\n\n### 初始化时序\n\n\n\n初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。\n\n主机和DS18B20做任何通讯前都需要对其初始化。\n\n初始化期间:\n\n1. 总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位\n\n2. 释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号\n\n\n\n![image-20210824154325250](https://gitee.com/ajream/images/raw/master/img/20210824154326_image-20210824154325250.png)\n\n\n\n\n\n### 写时序\n\n写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。\n\n所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。\n\n\n\n初始化写时序:\n\n总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。\n\n当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。\n\n\n\n进行写时序:\n\n总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0\n\n\n\n![image-20210824160214829](https://gitee.com/ajream/images/raw/master/img/20210824160216_image-20210824160214829.png)\n\n\n\n\n\n\n\n\n\n### 读时序\n\n1. 先把总线拉低至少1us\n2. 在15us内选取1us进行读数据\n3. 在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的\n4. 然后总线被释放\n5. 总时间必须>=60us\n\n![image-20210824161252256](https://gitee.com/ajream/images/raw/master/img/20210824161255_image-20210824161252256.png)\n\n\n\n读1的详细时序:\n\nTINIT,TRC 和 TSAMPLE 之和必须小于 15us\n\n\n\n![image-20210824162105674](https://gitee.com/ajream/images/raw/master/img/20210824162107_image-20210824162105674.png)\n\n\n\n推荐的读1时序:\n\n系统时间可以用下面办法达到最大:\n\nTINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。\n\n\n\n![image-20210824162319244](https://gitee.com/ajream/images/raw/master/img/20210824162320_image-20210824162319244.png)\n\n\n\n\n\n\n\n## 数据获取\n\n\n\n通过单线总线端口访问DS18B20的协议如下:\n\n\n\n1. 初始化\n\n2. ROM操作指令\n\n3. DS18B20功能指令、温度转换命令、读取暂存器命令\n\n\n\n部分常用ROM指令如下:\n\n![image-20210824163430400](https://gitee.com/ajream/images/raw/master/img/20210824163431_image-20210824163430400.png)\n\n\n\n\n\n## 例子\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit DS = P2^2; //DS18B20单总线\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/\nvoid Delay_us(uchar us)\n{\n\twhile(us--);\t\n}\n/*====================================\n函数:void Display(INT16U Value)\n参数:Value,显示值 取值0-65535\n描述:共阴极数码管显示函数可显示一个字节的数\n====================================*/\nvoid Display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型\n{\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\tP0 = table[Value/100];\t\t//数码管显示百位\n\tDU = 1;\t\t\t\t\t\t\t//打开段选\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tP0 = T_COM[0];\t\t\t\t //第一位数码管\n\tWE = 1;\t\t\t\t\t\t//打开位选\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%100/10]|0x80; //显示十位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t //第二位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%10];\t\t//显示个位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[2];\t\t\t\t//第三位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n}\n/*单总线初始化时序*/\nbit ds_init()\n{\n\tbit i;\n\tDS = 1;\n\t_nop_();\n\tDS = 0;\n\tDelay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位\n\tDS = 1; //释放总线\n\tDelay_us(4); //延时37.95us 等待18B20发回存在信号\n\ti = DS;\n\tDelay_us(20); //141.95us\n\tDS = 1;\n\t_nop_();\n\treturn (i);\n}\n/*写一个字节*/\nvoid write_byte(uchar dat)\n{\n\tuchar i;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生些时序\n\t\tDS = dat & 0x01;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1; //释放总线准备下一次数据写入\n\t\t_nop_();\n\t\tdat >>= 1;\n\t}\n}\n\nuchar read_byte()\n{\n\tuchar i, j, dat;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生读时序\n\t\tDS = 1;\n\t\t_nop_();//释放总线\n\t\tj = DS;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1;\n\t\t_nop_();\n\t\tdat = (j<<7)|(dat>>1);\t\n\t}\n\treturn (dat);\n}\nvoid main()\n{\n\tuint i;\n\tuchar L, M;\n/*\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令\n\twrite_byte(0x4e);//写暂存器指令\n\twrite_byte(0x7f);\n\twrite_byte(0xf7);\n\twrite_byte(0x1f);//配置工作在9位模式下\n\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令 \n\twrite_byte(0x48);*/\n\twhile(1)\n\t{\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0x44);//发送温度转换指令\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0xbe);//读取DS18B20暂存器值\n\t\tL = read_byte();\n\t\tM = read_byte();\n\t\ti = M;\n\t\ti <<= 8;\n\t\ti |= L;\t\t\t\t\t\t\n\t\ti = i * 0.0625 * 10 + 0.5;\n\t\tDisplay(i);\n\t}\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(六).md","raw":"---\ntitle: 51单片机学习笔记(六)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: DS18B20温度传感器介绍\nabbrlink: 5dd9c9e3\ndate: 2021-08-20 15:02:48\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n\n\n## DS18B20介绍\n\nDS18B20数字温度传感器是DALLAS公司生产的单总线器件,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。\n\n\n\n### 特点\n\n1. 通信采用1-Wire接口\n2. n每个DS18B20都有唯一的64位序列码储存在板载ROM中\n3. 无需外部元件\n4. 可从数据线供电,电源范围为3.0V ~ 5.5V。\n5. 可测量的温度范围在-55℃ ~ +125℃\n6. 在-10~+85℃范围内精确度为±0.5℃\n7. 温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃\n\n\n\n\n\n![image-20210824153229536](https://gitee.com/ajream/images/raw/master/img/20210824153232_image-20210824153229536.png)\n\n\n\n## 单总线\n\nDS18B20采用`1-wire Bus`传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。\n\n单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。\n\n所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。\n\n\n\n\n\n### 初始化时序\n\n\n\n初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。\n\n主机和DS18B20做任何通讯前都需要对其初始化。\n\n初始化期间:\n\n1. 总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位\n\n2. 释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号\n\n\n\n![image-20210824154325250](https://gitee.com/ajream/images/raw/master/img/20210824154326_image-20210824154325250.png)\n\n\n\n\n\n### 写时序\n\n写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。\n\n所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。\n\n\n\n初始化写时序:\n\n总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。\n\n当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。\n\n\n\n进行写时序:\n\n总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0\n\n\n\n![image-20210824160214829](https://gitee.com/ajream/images/raw/master/img/20210824160216_image-20210824160214829.png)\n\n\n\n\n\n\n\n\n\n### 读时序\n\n1. 先把总线拉低至少1us\n2. 在15us内选取1us进行读数据\n3. 在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的\n4. 然后总线被释放\n5. 总时间必须>=60us\n\n![image-20210824161252256](https://gitee.com/ajream/images/raw/master/img/20210824161255_image-20210824161252256.png)\n\n\n\n读1的详细时序:\n\nTINIT,TRC 和 TSAMPLE 之和必须小于 15us\n\n\n\n![image-20210824162105674](https://gitee.com/ajream/images/raw/master/img/20210824162107_image-20210824162105674.png)\n\n\n\n推荐的读1时序:\n\n系统时间可以用下面办法达到最大:\n\nTINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。\n\n\n\n![image-20210824162319244](https://gitee.com/ajream/images/raw/master/img/20210824162320_image-20210824162319244.png)\n\n\n\n\n\n\n\n## 数据获取\n\n\n\n通过单线总线端口访问DS18B20的协议如下:\n\n\n\n1. 初始化\n\n2. ROM操作指令\n\n3. DS18B20功能指令、温度转换命令、读取暂存器命令\n\n\n\n部分常用ROM指令如下:\n\n![image-20210824163430400](https://gitee.com/ajream/images/raw/master/img/20210824163431_image-20210824163430400.png)\n\n\n\n\n\n## 例子\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit DS = P2^2; //DS18B20单总线\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/\nvoid Delay_us(uchar us)\n{\n\twhile(us--);\t\n}\n/*====================================\n函数:void Display(INT16U Value)\n参数:Value,显示值 取值0-65535\n描述:共阴极数码管显示函数可显示一个字节的数\n====================================*/\nvoid Display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型\n{\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\tP0 = table[Value/100];\t\t//数码管显示百位\n\tDU = 1;\t\t\t\t\t\t\t//打开段选\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tP0 = T_COM[0];\t\t\t\t //第一位数码管\n\tWE = 1;\t\t\t\t\t\t//打开位选\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%100/10]|0x80; //显示十位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t //第二位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%10];\t\t//显示个位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[2];\t\t\t\t//第三位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n}\n/*单总线初始化时序*/\nbit ds_init()\n{\n\tbit i;\n\tDS = 1;\n\t_nop_();\n\tDS = 0;\n\tDelay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位\n\tDS = 1; //释放总线\n\tDelay_us(4); //延时37.95us 等待18B20发回存在信号\n\ti = DS;\n\tDelay_us(20); //141.95us\n\tDS = 1;\n\t_nop_();\n\treturn (i);\n}\n/*写一个字节*/\nvoid write_byte(uchar dat)\n{\n\tuchar i;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生些时序\n\t\tDS = dat & 0x01;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1; //释放总线准备下一次数据写入\n\t\t_nop_();\n\t\tdat >>= 1;\n\t}\n}\n\nuchar read_byte()\n{\n\tuchar i, j, dat;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生读时序\n\t\tDS = 1;\n\t\t_nop_();//释放总线\n\t\tj = DS;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1;\n\t\t_nop_();\n\t\tdat = (j<<7)|(dat>>1);\t\n\t}\n\treturn (dat);\n}\nvoid main()\n{\n\tuint i;\n\tuchar L, M;\n/*\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令\n\twrite_byte(0x4e);//写暂存器指令\n\twrite_byte(0x7f);\n\twrite_byte(0xf7);\n\twrite_byte(0x1f);//配置工作在9位模式下\n\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令 \n\twrite_byte(0x48);*/\n\twhile(1)\n\t{\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0x44);//发送温度转换指令\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0xbe);//读取DS18B20暂存器值\n\t\tL = read_byte();\n\t\tM = read_byte();\n\t\ti = M;\n\t\ti <<= 8;\n\t\ti |= L;\t\t\t\t\t\t\n\t\ti = i * 0.0625 * 10 + 0.5;\n\t\tDisplay(i);\n\t}\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(六)","published":1,"updated":"2021-09-10T12:45:19.484Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6od000oakveeb9m24v2","content":"

DS18B20介绍

DS18B20数字温度传感器是DALLAS公司生产的单总线器件,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。

\n

特点

    \n
  1. 通信采用1-Wire接口
  2. \n
  3. n每个DS18B20都有唯一的64位序列码储存在板载ROM中
  4. \n
  5. 无需外部元件
  6. \n
  7. 可从数据线供电,电源范围为3.0V ~ 5.5V。
  8. \n
  9. 可测量的温度范围在-55℃ ~ +125℃
  10. \n
  11. 在-10~+85℃范围内精确度为±0.5℃
  12. \n
  13. 温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃
  14. \n
\n

\"image-20210824153229536\"

\n

单总线

DS18B20采用1-wire Bus传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。

\n

单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。

\n

所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。

\n

初始化时序

初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。

\n

主机和DS18B20做任何通讯前都需要对其初始化。

\n

初始化期间:

\n
    \n
  1. 总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位

    \n
  2. \n
  3. 释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号

    \n
  4. \n
\n

\"image-20210824154325250\"

\n

写时序

写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。

\n

所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。

\n

初始化写时序:

\n

总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。

\n

当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。

\n

进行写时序:

\n

总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0

\n

\"image-20210824160214829\"

\n

读时序

    \n
  1. 先把总线拉低至少1us
  2. \n
  3. 在15us内选取1us进行读数据
  4. \n
  5. 在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的
  6. \n
  7. 然后总线被释放
  8. \n
  9. 总时间必须>=60us
  10. \n
\n

\"image-20210824161252256\"

\n

读1的详细时序:

\n

TINIT,TRC 和 TSAMPLE 之和必须小于 15us

\n

\"image-20210824162105674\"

\n

推荐的读1时序:

\n

系统时间可以用下面办法达到最大:

\n

TINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。

\n

\"image-20210824162319244\"

\n

数据获取

通过单线总线端口访问DS18B20的协议如下:

\n
    \n
  1. 初始化

    \n
  2. \n
  3. ROM操作指令

    \n
  4. \n
  5. DS18B20功能指令、温度转换命令、读取暂存器命令

    \n
  6. \n
\n

部分常用ROM指令如下:

\n

\"image-20210824163430400\"

\n

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <reg52.h>
#include <intrins.h>
#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit DS = P2^2; //DS18B20单总线
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
/*====================================
共阴极数码管段选码
====================================*/
uchar code table[]={
//0\t\t1\t 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
};

/*====================================
数码管位选码
====================================*/
\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数:void Delay_Ms(INT16U ms)
参数:ms,毫秒延时形参
描述:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void Delay_Ms(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}
/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/
void Delay_us(uchar us)
{
\twhile(us--);\t
}
/*====================================
函数:void Display(INT16U Value)
参数:Value,显示值 取值0-65535
描述:共阴极数码管显示函数可显示一个字节的数
====================================*/
void Display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型
{\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t//关闭段选
\tP0 = table[Value/100];\t\t//数码管显示百位
\tDU = 1;\t\t\t\t\t\t\t//打开段选
\tDU = 0;\t\t\t\t\t\t\t//关闭段选

\tWE = 0;\t\t\t\t\t\t//关闭位选
\tP0 = T_COM[0];\t\t\t\t //第一位数码管
\tWE = 1;\t\t\t\t\t\t//打开位选
\tWE = 0;\t\t\t\t\t\t//关闭位选
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Value%100/10]|0x80; //显示十位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[1];\t\t\t //第二位数码管
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Value%10];\t\t//显示个位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[2];\t\t\t\t//第三位数码管
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
}
/*单总线初始化时序*/
bit ds_init()
{
\tbit i;
\tDS = 1;
\t_nop_();
\tDS = 0;
\tDelay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位
\tDS = 1; //释放总线
\tDelay_us(4); //延时37.95us 等待18B20发回存在信号
\ti = DS;
\tDelay_us(20); //141.95us
\tDS = 1;
\t_nop_();
\treturn (i);
}
/*写一个字节*/
void write_byte(uchar dat)
{
\tuchar i;
\tfor(i=0;i<8;i++)
\t{
\t\tDS = 0;
\t\t_nop_();//产生些时序
\t\tDS = dat & 0x01;
\t\tDelay_us(10);//76.95us
\t\tDS = 1; //释放总线准备下一次数据写入
\t\t_nop_();
\t\tdat >>= 1;
\t}
}

uchar read_byte()
{
\tuchar i, j, dat;
\tfor(i=0;i<8;i++)
\t{
\t\tDS = 0;
\t\t_nop_();//产生读时序
\t\tDS = 1;
\t\t_nop_();//释放总线
\t\tj = DS;
\t\tDelay_us(10);//76.95us
\t\tDS = 1;
\t\t_nop_();
\t\tdat = (j<<7)|(dat>>1);\t
\t}
\treturn (dat);
}
void main()
{
\tuint i;
\tuchar L, M;
/*\tds_init();//初始化DS18B20
\twrite_byte(0xcc);//发送跳跃ROM指令
\twrite_byte(0x4e);//写暂存器指令
\twrite_byte(0x7f);
\twrite_byte(0xf7);
\twrite_byte(0x1f);//配置工作在9位模式下
\tds_init();//初始化DS18B20
\twrite_byte(0xcc);//发送跳跃ROM指令
\twrite_byte(0x48);*/
\twhile(1)
\t{
\t\tds_init();//初始化DS18B20
\t\twrite_byte(0xcc);//发送跳跃ROM指令
\t\twrite_byte(0x44);//发送温度转换指令
\t\tds_init();//初始化DS18B20
\t\twrite_byte(0xcc);//发送跳跃ROM指令
\t\twrite_byte(0xbe);//读取DS18B20暂存器值
\t\tL = read_byte();
\t\tM = read_byte();
\t\ti = M;
\t\ti <<= 8;
\t\ti |= L;\t\t\t\t\t\t
\t\ti = i * 0.0625 * 10 + 0.5;
\t\tDisplay(i);
\t}
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

DS18B20介绍

DS18B20数字温度传感器是DALLAS公司生产的单总线器件,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。

\n

特点

    \n
  1. 通信采用1-Wire接口
  2. \n
  3. n每个DS18B20都有唯一的64位序列码储存在板载ROM中
  4. \n
  5. 无需外部元件
  6. \n
  7. 可从数据线供电,电源范围为3.0V ~ 5.5V。
  8. \n
  9. 可测量的温度范围在-55℃ ~ +125℃
  10. \n
  11. 在-10~+85℃范围内精确度为±0.5℃
  12. \n
  13. 温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃
  14. \n
\n

\"image-20210824153229536\"

\n

单总线

DS18B20采用1-wire Bus传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。

\n

单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。

\n

所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。

\n

初始化时序

初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。

\n

主机和DS18B20做任何通讯前都需要对其初始化。

\n

初始化期间:

\n
    \n
  1. 总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位

    \n
  2. \n
  3. 释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号

    \n
  4. \n
\n

\"image-20210824154325250\"

\n

写时序

写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。

\n

所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。

\n

初始化写时序:

\n

总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。

\n

当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。

\n

进行写时序:

\n

总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0

\n

\"image-20210824160214829\"

\n

读时序

    \n
  1. 先把总线拉低至少1us
  2. \n
  3. 在15us内选取1us进行读数据
  4. \n
  5. 在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的
  6. \n
  7. 然后总线被释放
  8. \n
  9. 总时间必须>=60us
  10. \n
\n

\"image-20210824161252256\"

\n

读1的详细时序:

\n

TINIT,TRC 和 TSAMPLE 之和必须小于 15us

\n

\"image-20210824162105674\"

\n

推荐的读1时序:

\n

系统时间可以用下面办法达到最大:

\n

TINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。

\n

\"image-20210824162319244\"

\n

数据获取

通过单线总线端口访问DS18B20的协议如下:

\n
    \n
  1. 初始化

    \n
  2. \n
  3. ROM操作指令

    \n
  4. \n
  5. DS18B20功能指令、温度转换命令、读取暂存器命令

    \n
  6. \n
\n

部分常用ROM指令如下:

\n

\"image-20210824163430400\"

\n

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <reg52.h>
#include <intrins.h>
#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit DS = P2^2; //DS18B20单总线
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
/*====================================
共阴极数码管段选码
====================================*/
uchar code table[]={
//0\t\t1\t 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
};

/*====================================
数码管位选码
====================================*/
\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数:void Delay_Ms(INT16U ms)
参数:ms,毫秒延时形参
描述:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void Delay_Ms(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}
/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/
void Delay_us(uchar us)
{
\twhile(us--);\t
}
/*====================================
函数:void Display(INT16U Value)
参数:Value,显示值 取值0-65535
描述:共阴极数码管显示函数可显示一个字节的数
====================================*/
void Display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型
{\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t//关闭段选
\tP0 = table[Value/100];\t\t//数码管显示百位
\tDU = 1;\t\t\t\t\t\t\t//打开段选
\tDU = 0;\t\t\t\t\t\t\t//关闭段选

\tWE = 0;\t\t\t\t\t\t//关闭位选
\tP0 = T_COM[0];\t\t\t\t //第一位数码管
\tWE = 1;\t\t\t\t\t\t//打开位选
\tWE = 0;\t\t\t\t\t\t//关闭位选
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Value%100/10]|0x80; //显示十位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[1];\t\t\t //第二位数码管
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Value%10];\t\t//显示个位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[2];\t\t\t\t//第三位数码管
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
}
/*单总线初始化时序*/
bit ds_init()
{
\tbit i;
\tDS = 1;
\t_nop_();
\tDS = 0;
\tDelay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位
\tDS = 1; //释放总线
\tDelay_us(4); //延时37.95us 等待18B20发回存在信号
\ti = DS;
\tDelay_us(20); //141.95us
\tDS = 1;
\t_nop_();
\treturn (i);
}
/*写一个字节*/
void write_byte(uchar dat)
{
\tuchar i;
\tfor(i=0;i<8;i++)
\t{
\t\tDS = 0;
\t\t_nop_();//产生些时序
\t\tDS = dat & 0x01;
\t\tDelay_us(10);//76.95us
\t\tDS = 1; //释放总线准备下一次数据写入
\t\t_nop_();
\t\tdat >>= 1;
\t}
}

uchar read_byte()
{
\tuchar i, j, dat;
\tfor(i=0;i<8;i++)
\t{
\t\tDS = 0;
\t\t_nop_();//产生读时序
\t\tDS = 1;
\t\t_nop_();//释放总线
\t\tj = DS;
\t\tDelay_us(10);//76.95us
\t\tDS = 1;
\t\t_nop_();
\t\tdat = (j<<7)|(dat>>1);\t
\t}
\treturn (dat);
}
void main()
{
\tuint i;
\tuchar L, M;
/*\tds_init();//初始化DS18B20
\twrite_byte(0xcc);//发送跳跃ROM指令
\twrite_byte(0x4e);//写暂存器指令
\twrite_byte(0x7f);
\twrite_byte(0xf7);
\twrite_byte(0x1f);//配置工作在9位模式下
\tds_init();//初始化DS18B20
\twrite_byte(0xcc);//发送跳跃ROM指令
\twrite_byte(0x48);*/
\twhile(1)
\t{
\t\tds_init();//初始化DS18B20
\t\twrite_byte(0xcc);//发送跳跃ROM指令
\t\twrite_byte(0x44);//发送温度转换指令
\t\tds_init();//初始化DS18B20
\t\twrite_byte(0xcc);//发送跳跃ROM指令
\t\twrite_byte(0xbe);//读取DS18B20暂存器值
\t\tL = read_byte();
\t\tM = read_byte();
\t\ti = M;
\t\ti <<= 8;
\t\ti |= L;\t\t\t\t\t\t
\t\ti = i * 0.0625 * 10 + 0.5;
\t\tDisplay(i);
\t}
}
\n"},{"title":"Github Actions自动部署","date":"2021-09-13T12:13:46.000Z","description":"使用Github的Actions功能自动部署Hexo博客","cover":"https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png","abbrlink":"88ad757","_content":"\n\n\n## 写在前面\n\n\n\n\n\n没用过Actions的建议细看,好好理解其工作原理和流程\n\n本文大部分参考 [糖果屋] 的教程,详见[糖果屋-使用Github Action实现全自动部署](https://akilar.top/posts/f752c86d/), 感谢大佬!\n\n## Github Actions简介\n\nGithub Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句\n\n在这里可以让脚本自动执行 `hexo cl && hexo g -d` 三连操作\n\n\n\n\n\n\n\n## 获取令牌(Token)\n\n为了确保交由 `Github Action` 来持续部署时,`Github Action` 具备足够的权限来进行 `hexo deploy` 操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 `Token`,博主分别在 `Github`、`Gitee`、`Coding` 处部署了静态页面,所以也就需要获取这三处的 `Token`。\n\n当我们获取了token后,在 `_config.yml`文件的 `deploy` 项就可以使用了\n\n{%tabs test,-1%}\n\n\n\n访问 [Github-> 头像(右上角)->Settings->Developer Settings->Personal access tokens](https://github.com/settings/tokens)->generate new token, 创建的 `Token` 名称随意,但必须勾选 repo 项。\n\n![image-20210913220005116](https://gitee.com/ajream/images/raw/master/img/20210913220008_image-20210913220005116.png)\n\n![image-20210913220422469](https://gitee.com/ajream/images/raw/master/img/20210913220423_image-20210913220422469.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n访问 [Gitee-> 头像(右上角)-> 设置 -> 私人令牌](https://gitee.com/profile/personal_access_tokens) -> 生成新令牌\n\n![image-20210913221257282](https://gitee.com/ajream/images/raw/master/img/20210913221258_image-20210913221257282.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n\n\n访问 [Coding](https://coding.net/)-> 头像(右上角)-> 个人账户设置 -> 访问令牌 -> 新建令牌。\n\n![image-20210913221822733](https://gitee.com/ajream/images/raw/master/img/20210913221824_image-20210913221822733.png)\n\n\n\n\n\n![image-20210913221754924](https://gitee.com/ajream/images/raw/master/img/20210913221756_image-20210913221754924.png)\n\n\n\n{% note warning modern %}\n\n注意coding平台与Github、Gitee不同,它生成的令牌还有【令牌用户名】,并且只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`和`令牌用户名`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n{%endtabs%}\n\n\n\n\n\n\n\n## 创建存放源码的私有仓库\n\n{%folding blue, 展开%}\n\n我们需要创建一个用来存放 `Hexo` 博客源码的私有仓库 `[SourceRepo]`,这点在 [Win10](https://akilar.top/posts/6ef63e2d/) 的 `Hexo` 博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)\n\n\n创建完成后,需要把博客的源码 `push` 到这里。首先获取远程仓库地址,此处虽然 `SSH` 和 `HTTPS` 均可。`SSH` 在绑定过 `ssh key` 的设备上无需再输入密码,`HTTPS` 则需要输入密码,但是 `SSH` 偶尔会遇到端口占用的情况。请自主选择。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)\n\n\n\n{%note default modern%}\n\n这里之所以是私有仓库,是因为在接下来的配置中会用到 `Token`,如果 `Token` 被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n## 配置 deploy 项\n\n\n\n打开站点配置文件 `[Blogroot]/_config.yml`, 找到 `deploy` 配置项,使用之前生成的 `[SiteToken]` 和各个站点仓库 `URL` 来组装地址。\n\n\n\n```yml\ndeploy:\n- type: git\n repo:\n gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch]\n gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch]\n coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]\n # [,branch]为可选项,表示部署的分支\n #2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n{%note default modern%}\n\n注意:Gitee的用户名如果含有大写字母,要改为小写; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次\n\n{%endnote%}\n\n\n\n参考我个人的写法:\n\n\n\n```yml\ndeploy:\n type: git\n repo: \n github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master\n gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master\n coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master\n```\n\n\n\n\n\n## 配置 Github Action\n\n\n\n在 `[Blogroot]` 新建`.github` 文件夹,注意开头是有个`.` 的。然后在`.github` 内新建 `workflows` 文件夹,再在 `workflows` 文件夹内新建 `autodeploy.yml`, 在 `[Blogroot]/.github/workflows/autodeploy.yml` 里面输入\n\n```yml\n# 当有改动推送到master分支时,启动Action\nname: 自动部署\n\non:\n push:\n branches:\n - master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n release:\n types:\n - published\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - name: 检查分支\n uses: actions/checkout@v2\n with:\n ref: master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n - name: 安装 Node\n uses: actions/setup-node@v1\n with:\n node-version: \"12.x\"\n\n - name: 安装 Hexo\n run: |\n export TZ='Asia/Shanghai'\n npm install hexo-cli -g\n\n - name: 缓存 Hexo\n uses: actions/cache@v1\n id: cache\n with:\n path: node_modules\n key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}\n\n - name: 安装依赖\n if: steps.cache.outputs.cache-hit != 'true'\n run: |\n npm install --save\n\n - name: 生成静态文件\n run: |\n hexo clean\n hexo generate\n\n - name: 部署\n run: |\n git config --global user.name \"[GithubUsername]\"\n git config --global user.email \"[GithubEmail]\"\n git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git\n # 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患\n # =====注意.deploy_git前面有个空格=====\n # 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件\n hexo deploy\n```\n\n\n\n{%note info modern%}\n\n注意看代码里面的注释\n\n{%endnote%}\n\n\n\n\n\n## 重新设置远程仓库和分支\n\n\n\n{%tabs gggg, -1%}\n\n\n\n1. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git\n ```\n\n 如果不是 `butterfly` 主题,记得替换最后一行内容为你自己当前使用的主题\n\n2. 提交源码到刚才新建的私有仓库 \n\n 在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。\n\n ```sh\n git remote rm origin # 删除原有仓库链接\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为新的存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n git add .\n git commit -m \"github action update\"\n git push origin master\n \n #2020年10月后github新建仓库默认分支改为main,注意更改\n ```\n\n\n\n> 可能遇到的 bug\n> 因为 `butterfly` 主题文件夹下的`.git` 文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 `themes` 文件夹移动到别的目录下。然后 `commit` 一次。接着将 `themes` 文件夹移动回来,再 `commit` 一次。\n\n\n\n\n\n\n\n\n\n1. 删除或者先把 `[Blogroot]/themes/butterfly/.git` 移动到非博客文件夹目录下,原因是主题文件夹下的`.git` 文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。\n\n2. 在博客根目录 `[Blogroot]` 路径下运行指令\n\n ```sh\n git init #初始化\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n ```\n\n3. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git # 如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题\n ```\n\n\n\n完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署\n\n```sh\ngit add .\ngit commit -m \"github action update\"\ngit push origin master\n#2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的`.git` 放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)\n\n\n\n{%endtabs%}\n\n\n\n\n\n## 查看部署情况\n\n![image-20210913232006065](https://gitee.com/ajream/images/raw/master/img/20210913232007_image-20210913232006065.png)\n\n\n\n\n\n点击正在运行的workflow就可以查看部署情况\n\n![image-20210913233828678](https://gitee.com/ajream/images/raw/master/img/20210913233832_image-20210913233828678.png)\n\n\n\n![image-20210913234018254](https://gitee.com/ajream/images/raw/master/img/20210913234019_image-20210913234018254.png)\n\n## 我遇到的Bug\n\n\n\nBug:\n\n**Spawn Failed**\n\n原因主要就是配置deploy项时仓库的git链接没有正确配置,比如 :\n\n1. gitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的)\n2. 配置coding时没有仔细看教程,用户名必须要使用生成 `token` 时系统同时生成的令牌用户名\n\n\n\n\n\n**看板娘不能显示**\n\n删除 `BlogRoot/themes/butterfly/source/live2d-widget` 下的 `.git` 文件\n\n然后执行:\n\n```\ngit rm --cached /themes/butterfly/source/live2d-widget\ngit add .\ngit commit -m \"修复看板娘不能正常显示\"\ngit push origin master\n```\n\n \n\n","source":"_posts/Hexo/github Actions自动部署.md","raw":"---\ntitle: Github Actions自动部署\ndate: '2021-09-13 20:13:46'\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: 使用Github的Actions功能自动部署Hexo博客\ncover: https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png\nabbrlink: 88ad757\n---\n\n\n\n## 写在前面\n\n\n\n\n\n没用过Actions的建议细看,好好理解其工作原理和流程\n\n本文大部分参考 [糖果屋] 的教程,详见[糖果屋-使用Github Action实现全自动部署](https://akilar.top/posts/f752c86d/), 感谢大佬!\n\n## Github Actions简介\n\nGithub Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句\n\n在这里可以让脚本自动执行 `hexo cl && hexo g -d` 三连操作\n\n\n\n\n\n\n\n## 获取令牌(Token)\n\n为了确保交由 `Github Action` 来持续部署时,`Github Action` 具备足够的权限来进行 `hexo deploy` 操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 `Token`,博主分别在 `Github`、`Gitee`、`Coding` 处部署了静态页面,所以也就需要获取这三处的 `Token`。\n\n当我们获取了token后,在 `_config.yml`文件的 `deploy` 项就可以使用了\n\n{%tabs test,-1%}\n\n\n\n访问 [Github-> 头像(右上角)->Settings->Developer Settings->Personal access tokens](https://github.com/settings/tokens)->generate new token, 创建的 `Token` 名称随意,但必须勾选 repo 项。\n\n![image-20210913220005116](https://gitee.com/ajream/images/raw/master/img/20210913220008_image-20210913220005116.png)\n\n![image-20210913220422469](https://gitee.com/ajream/images/raw/master/img/20210913220423_image-20210913220422469.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n访问 [Gitee-> 头像(右上角)-> 设置 -> 私人令牌](https://gitee.com/profile/personal_access_tokens) -> 生成新令牌\n\n![image-20210913221257282](https://gitee.com/ajream/images/raw/master/img/20210913221258_image-20210913221257282.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n\n\n访问 [Coding](https://coding.net/)-> 头像(右上角)-> 个人账户设置 -> 访问令牌 -> 新建令牌。\n\n![image-20210913221822733](https://gitee.com/ajream/images/raw/master/img/20210913221824_image-20210913221822733.png)\n\n\n\n\n\n![image-20210913221754924](https://gitee.com/ajream/images/raw/master/img/20210913221756_image-20210913221754924.png)\n\n\n\n{% note warning modern %}\n\n注意coding平台与Github、Gitee不同,它生成的令牌还有【令牌用户名】,并且只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`和`令牌用户名`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n{%endtabs%}\n\n\n\n\n\n\n\n## 创建存放源码的私有仓库\n\n{%folding blue, 展开%}\n\n我们需要创建一个用来存放 `Hexo` 博客源码的私有仓库 `[SourceRepo]`,这点在 [Win10](https://akilar.top/posts/6ef63e2d/) 的 `Hexo` 博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)\n\n\n创建完成后,需要把博客的源码 `push` 到这里。首先获取远程仓库地址,此处虽然 `SSH` 和 `HTTPS` 均可。`SSH` 在绑定过 `ssh key` 的设备上无需再输入密码,`HTTPS` 则需要输入密码,但是 `SSH` 偶尔会遇到端口占用的情况。请自主选择。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)\n\n\n\n{%note default modern%}\n\n这里之所以是私有仓库,是因为在接下来的配置中会用到 `Token`,如果 `Token` 被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n## 配置 deploy 项\n\n\n\n打开站点配置文件 `[Blogroot]/_config.yml`, 找到 `deploy` 配置项,使用之前生成的 `[SiteToken]` 和各个站点仓库 `URL` 来组装地址。\n\n\n\n```yml\ndeploy:\n- type: git\n repo:\n gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch]\n gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch]\n coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]\n # [,branch]为可选项,表示部署的分支\n #2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n{%note default modern%}\n\n注意:Gitee的用户名如果含有大写字母,要改为小写; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次\n\n{%endnote%}\n\n\n\n参考我个人的写法:\n\n\n\n```yml\ndeploy:\n type: git\n repo: \n github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master\n gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master\n coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master\n```\n\n\n\n\n\n## 配置 Github Action\n\n\n\n在 `[Blogroot]` 新建`.github` 文件夹,注意开头是有个`.` 的。然后在`.github` 内新建 `workflows` 文件夹,再在 `workflows` 文件夹内新建 `autodeploy.yml`, 在 `[Blogroot]/.github/workflows/autodeploy.yml` 里面输入\n\n```yml\n# 当有改动推送到master分支时,启动Action\nname: 自动部署\n\non:\n push:\n branches:\n - master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n release:\n types:\n - published\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - name: 检查分支\n uses: actions/checkout@v2\n with:\n ref: master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n - name: 安装 Node\n uses: actions/setup-node@v1\n with:\n node-version: \"12.x\"\n\n - name: 安装 Hexo\n run: |\n export TZ='Asia/Shanghai'\n npm install hexo-cli -g\n\n - name: 缓存 Hexo\n uses: actions/cache@v1\n id: cache\n with:\n path: node_modules\n key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}\n\n - name: 安装依赖\n if: steps.cache.outputs.cache-hit != 'true'\n run: |\n npm install --save\n\n - name: 生成静态文件\n run: |\n hexo clean\n hexo generate\n\n - name: 部署\n run: |\n git config --global user.name \"[GithubUsername]\"\n git config --global user.email \"[GithubEmail]\"\n git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git\n # 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患\n # =====注意.deploy_git前面有个空格=====\n # 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件\n hexo deploy\n```\n\n\n\n{%note info modern%}\n\n注意看代码里面的注释\n\n{%endnote%}\n\n\n\n\n\n## 重新设置远程仓库和分支\n\n\n\n{%tabs gggg, -1%}\n\n\n\n1. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git\n ```\n\n 如果不是 `butterfly` 主题,记得替换最后一行内容为你自己当前使用的主题\n\n2. 提交源码到刚才新建的私有仓库 \n\n 在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。\n\n ```sh\n git remote rm origin # 删除原有仓库链接\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为新的存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n git add .\n git commit -m \"github action update\"\n git push origin master\n \n #2020年10月后github新建仓库默认分支改为main,注意更改\n ```\n\n\n\n> 可能遇到的 bug\n> 因为 `butterfly` 主题文件夹下的`.git` 文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 `themes` 文件夹移动到别的目录下。然后 `commit` 一次。接着将 `themes` 文件夹移动回来,再 `commit` 一次。\n\n\n\n\n\n\n\n\n\n1. 删除或者先把 `[Blogroot]/themes/butterfly/.git` 移动到非博客文件夹目录下,原因是主题文件夹下的`.git` 文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。\n\n2. 在博客根目录 `[Blogroot]` 路径下运行指令\n\n ```sh\n git init #初始化\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n ```\n\n3. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git # 如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题\n ```\n\n\n\n完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署\n\n```sh\ngit add .\ngit commit -m \"github action update\"\ngit push origin master\n#2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的`.git` 放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)\n\n\n\n{%endtabs%}\n\n\n\n\n\n## 查看部署情况\n\n![image-20210913232006065](https://gitee.com/ajream/images/raw/master/img/20210913232007_image-20210913232006065.png)\n\n\n\n\n\n点击正在运行的workflow就可以查看部署情况\n\n![image-20210913233828678](https://gitee.com/ajream/images/raw/master/img/20210913233832_image-20210913233828678.png)\n\n\n\n![image-20210913234018254](https://gitee.com/ajream/images/raw/master/img/20210913234019_image-20210913234018254.png)\n\n## 我遇到的Bug\n\n\n\nBug:\n\n**Spawn Failed**\n\n原因主要就是配置deploy项时仓库的git链接没有正确配置,比如 :\n\n1. gitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的)\n2. 配置coding时没有仔细看教程,用户名必须要使用生成 `token` 时系统同时生成的令牌用户名\n\n\n\n\n\n**看板娘不能显示**\n\n删除 `BlogRoot/themes/butterfly/source/live2d-widget` 下的 `.git` 文件\n\n然后执行:\n\n```\ngit rm --cached /themes/butterfly/source/live2d-widget\ngit add .\ngit commit -m \"修复看板娘不能正常显示\"\ngit push origin master\n```\n\n \n\n","slug":"Hexo/github Actions自动部署","published":1,"updated":"2021-09-16T15:29:49.622Z","_id":"cktk8o6oe000rakve4wdsbjyw","comments":1,"layout":"post","photos":[],"link":"","content":"

写在前面

\n

没用过Actions的建议细看,好好理解其工作原理和流程

\n

本文大部分参考 [糖果屋] 的教程,详见糖果屋-使用Github Action实现全自动部署, 感谢大佬!

\n

Github Actions简介

\n

Github Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句

\n

在这里可以让脚本自动执行 hexo cl && hexo g -d 三连操作

\n

获取令牌(Token)

\n

为了确保交由 Github Action 来持续部署时,Github Action 具备足够的权限来进行 hexo deploy 操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 Token,博主分别在 GithubGiteeCoding 处部署了静态页面,所以也就需要获取这三处的 Token

\n

当我们获取了token后,在 _config.yml文件的 deploy 项就可以使用了

\n

访问 Github-> 头像(右上角)->Settings->Developer Settings->Personal access tokens->generate new token, 创建的 Token 名称随意,但必须勾选 repo 项。

\n

\"image-20210913220005116\"

\n

\"image-20210913220422469\"

\n

token 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token。之后如果忘记了就只能重新生成重新配置了。

\n

访问 Gitee-> 头像(右上角)-> 设置 -> 私人令牌 -> 生成新令牌

\n

\"image-20210913221257282\"

\n

token 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token。之后如果忘记了就只能重新生成重新配置了。

\n

访问 Coding-> 头像(右上角)-> 个人账户设置 -> 访问令牌 -> 新建令牌。

\n

\"image-20210913221822733\"

\n

\"image-20210913221754924\"

\n

注意coding平台与Github、Gitee不同,它生成的令牌还有【令牌用户名】,并且只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token令牌用户名。之后如果忘记了就只能重新生成重新配置了。

\n
\n

创建存放源码的私有仓库

\n
展开 \n
\n

我们需要创建一个用来存放 Hexo 博客源码的私有仓库 [SourceRepo],这点在 Win10Hexo 博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。
\"img\"

创建完成后,需要把博客的源码 push 到这里。首先获取远程仓库地址,此处虽然 SSHHTTPS 均可。SSH 在绑定过 ssh key 的设备上无需再输入密码,HTTPS 则需要输入密码,但是 SSH 偶尔会遇到端口占用的情况。请自主选择。
\"img\"

这里之所以是私有仓库,是因为在接下来的配置中会用到 Token,如果 Token 被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。

\n
\n
\n

配置 deploy 项

\n

打开站点配置文件 [Blogroot]/_config.yml, 找到 deploy 配置项,使用之前生成的 [SiteToken] 和各个站点仓库 URL 来组装地址。

\n
1
2
3
4
5
6
7
8
deploy:
- type: git
repo:
gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch]
gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch]
coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]
# [,branch]为可选项,表示部署的分支
#2020年10月后github新建仓库默认分支改为main,注意更改
\n

注意:Gitee的用户名如果含有大写字母,要改为小写; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次

\n
\n

参考我个人的写法:

\n
1
2
3
4
5
6
deploy:
type: git
repo:
github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master
gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master
coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master
\n

配置 Github Action

\n

[Blogroot] 新建.github 文件夹,注意开头是有个. 的。然后在.github 内新建 workflows 文件夹,再在 workflows 文件夹内新建 autodeploy.yml, 在 [Blogroot]/.github/workflows/autodeploy.yml 里面输入

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 当有改动推送到master分支时,启动Action
name: 自动部署

on:
push:
branches:
- master #2020年10月后github新建仓库默认分支改为main,注意更改

release:
types:
- published

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 检查分支
uses: actions/checkout@v2
with:
ref: master #2020年10月后github新建仓库默认分支改为main,注意更改

- name: 安装 Node
uses: actions/setup-node@v1
with:
node-version: "12.x"

- name: 安装 Hexo
run: |
export TZ='Asia/Shanghai'
npm install hexo-cli -g

- name: 缓存 Hexo
uses: actions/cache@v1
id: cache
with:
path: node_modules
key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}

- name: 安装依赖
if: steps.cache.outputs.cache-hit != 'true'
run: |
npm install --save

- name: 生成静态文件
run: |
hexo clean
hexo generate

- name: 部署
run: |
git config --global user.name "[GithubUsername]"
git config --global user.email "[GithubEmail]"
git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git
# 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患
# =====注意.deploy_git前面有个空格=====
# 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件
hexo deploy
\n

注意看代码里面的注释

\n
\n

重新设置远程仓库和分支

\n
    \n
  1. \n

    添加屏蔽项
    \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。
    \n打开 [Blogroot]/.gitignore, 输入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .DS_Store
    Thumbs.db
    db.json
    *.log
    node_modules/
    public/
    .deploy*/
    .deploy_git*/
    .idea
    themes/butterfly/.git
    \n

    如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题

    \n
  2. \n
  3. \n

    提交源码到刚才新建的私有仓库

    \n

    在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    git remote rm origin # 删除原有仓库链接
    git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为新的存放源码的github私有仓库
    git checkout -b master # 切换到master分支,
    #2020年10月后github新建仓库默认分支改为main,注意更改
    # 如果不是,后面的所有设置的分支记得保持一致
    git add .
    git commit -m "github action update"
    git push origin master

    #2020年10月后github新建仓库默认分支改为main,注意更改
    \n
  4. \n
\n
\n

可能遇到的 bug
\n因为 butterfly 主题文件夹下的.git 文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 themes 文件夹移动到别的目录下。然后 commit 一次。接着将 themes 文件夹移动回来,再 commit 一次。

\n
    \n
  1. \n

    删除或者先把 [Blogroot]/themes/butterfly/.git 移动到非博客文件夹目录下,原因是主题文件夹下的.git 文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。

    \n
  2. \n
  3. \n

    在博客根目录 [Blogroot] 路径下运行指令

    \n
    1
    2
    3
    4
    5
    git init #初始化
    git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为存放源码的github私有仓库
    git checkout -b master # 切换到master分支,
    #2020年10月后github新建仓库默认分支改为main,注意更改
    # 如果不是,后面的所有设置的分支记得保持一致
    \n
  4. \n
  5. \n

    添加屏蔽项
    \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。
    \n打开 [Blogroot]/.gitignore, 输入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .DS_Store
    Thumbs.db
    db.json
    *.log
    node_modules/
    public/
    .deploy*/
    .deploy_git*/
    .idea
    themes/butterfly/.git # 如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题
    \n
  6. \n
\n

完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署

\n
1
2
3
4
git add .
git commit -m "github action update"
git push origin master
#2020年10月后github新建仓库默认分支改为main,注意更改
\n

此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的.git 放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)

\n

查看部署情况

\n

\"image-20210913232006065\"

\n

点击正在运行的workflow就可以查看部署情况

\n

\"image-20210913233828678\"

\n

\"image-20210913234018254\"

\n

我遇到的Bug

\n

Bug:

\n

Spawn Failed

\n

原因主要就是配置deploy项时仓库的git链接没有正确配置,比如 :

\n
    \n
  1. gitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的)
  2. \n
  3. 配置coding时没有仔细看教程,用户名必须要使用生成 token 时系统同时生成的令牌用户名
  4. \n
\n

看板娘不能显示

\n

删除 BlogRoot/themes/butterfly/source/live2d-widget 下的 .git 文件

\n

然后执行:

\n
1
2
3
4
git rm --cached /themes/butterfly/source/live2d-widget
git add .
git commit -m "修复看板娘不能正常显示"
git push origin master
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

写在前面

\n

没用过Actions的建议细看,好好理解其工作原理和流程

\n

本文大部分参考 [糖果屋] 的教程,详见糖果屋-使用Github Action实现全自动部署, 感谢大佬!

\n

Github Actions简介

\n

Github Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句

\n

在这里可以让脚本自动执行 hexo cl && hexo g -d 三连操作

\n

获取令牌(Token)

\n

为了确保交由 Github Action 来持续部署时,Github Action 具备足够的权限来进行 hexo deploy 操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 Token,博主分别在 GithubGiteeCoding 处部署了静态页面,所以也就需要获取这三处的 Token

\n

当我们获取了token后,在 _config.yml文件的 deploy 项就可以使用了

\n

访问 Github-> 头像(右上角)->Settings->Developer Settings->Personal access tokens->generate new token, 创建的 Token 名称随意,但必须勾选 repo 项。

\n

\"image-20210913220005116\"

\n

\"image-20210913220422469\"

\n

token 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token。之后如果忘记了就只能重新生成重新配置了。

\n

访问 Gitee-> 头像(右上角)-> 设置 -> 私人令牌 -> 生成新令牌

\n

\"image-20210913221257282\"

\n

token 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token。之后如果忘记了就只能重新生成重新配置了。

\n

访问 Coding-> 头像(右上角)-> 个人账户设置 -> 访问令牌 -> 新建令牌。

\n

\"image-20210913221822733\"

\n

\"image-20210913221754924\"

\n

注意coding平台与Github、Gitee不同,它生成的令牌还有【令牌用户名】,并且只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token令牌用户名。之后如果忘记了就只能重新生成重新配置了。

\n
\n

创建存放源码的私有仓库

\n
展开 \n
\n

我们需要创建一个用来存放 Hexo 博客源码的私有仓库 [SourceRepo],这点在 Win10Hexo 博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。
\"img\"

创建完成后,需要把博客的源码 push 到这里。首先获取远程仓库地址,此处虽然 SSHHTTPS 均可。SSH 在绑定过 ssh key 的设备上无需再输入密码,HTTPS 则需要输入密码,但是 SSH 偶尔会遇到端口占用的情况。请自主选择。
\"img\"

这里之所以是私有仓库,是因为在接下来的配置中会用到 Token,如果 Token 被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。

\n
\n
\n

配置 deploy 项

\n

打开站点配置文件 [Blogroot]/_config.yml, 找到 deploy 配置项,使用之前生成的 [SiteToken] 和各个站点仓库 URL 来组装地址。

\n
1
2
3
4
5
6
7
8
deploy:
- type: git
repo:
gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch]
gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch]
coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]
# [,branch]为可选项,表示部署的分支
#2020年10月后github新建仓库默认分支改为main,注意更改
\n

注意:Gitee的用户名如果含有大写字母,要改为小写; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次

\n
\n

参考我个人的写法:

\n
1
2
3
4
5
6
deploy:
type: git
repo:
github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master
gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master
coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master
\n

配置 Github Action

\n

[Blogroot] 新建.github 文件夹,注意开头是有个. 的。然后在.github 内新建 workflows 文件夹,再在 workflows 文件夹内新建 autodeploy.yml, 在 [Blogroot]/.github/workflows/autodeploy.yml 里面输入

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 当有改动推送到master分支时,启动Action
name: 自动部署

on:
push:
branches:
- master #2020年10月后github新建仓库默认分支改为main,注意更改

release:
types:
- published

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 检查分支
uses: actions/checkout@v2
with:
ref: master #2020年10月后github新建仓库默认分支改为main,注意更改

- name: 安装 Node
uses: actions/setup-node@v1
with:
node-version: "12.x"

- name: 安装 Hexo
run: |
export TZ='Asia/Shanghai'
npm install hexo-cli -g

- name: 缓存 Hexo
uses: actions/cache@v1
id: cache
with:
path: node_modules
key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}

- name: 安装依赖
if: steps.cache.outputs.cache-hit != 'true'
run: |
npm install --save

- name: 生成静态文件
run: |
hexo clean
hexo generate

- name: 部署
run: |
git config --global user.name "[GithubUsername]"
git config --global user.email "[GithubEmail]"
git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git
# 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患
# =====注意.deploy_git前面有个空格=====
# 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件
hexo deploy
\n

注意看代码里面的注释

\n
\n

重新设置远程仓库和分支

\n
    \n
  1. \n

    添加屏蔽项
    \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。
    \n打开 [Blogroot]/.gitignore, 输入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .DS_Store
    Thumbs.db
    db.json
    *.log
    node_modules/
    public/
    .deploy*/
    .deploy_git*/
    .idea
    themes/butterfly/.git
    \n

    如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题

    \n
  2. \n
  3. \n

    提交源码到刚才新建的私有仓库

    \n

    在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    git remote rm origin # 删除原有仓库链接
    git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为新的存放源码的github私有仓库
    git checkout -b master # 切换到master分支,
    #2020年10月后github新建仓库默认分支改为main,注意更改
    # 如果不是,后面的所有设置的分支记得保持一致
    git add .
    git commit -m "github action update"
    git push origin master

    #2020年10月后github新建仓库默认分支改为main,注意更改
    \n
  4. \n
\n
\n

可能遇到的 bug
\n因为 butterfly 主题文件夹下的.git 文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 themes 文件夹移动到别的目录下。然后 commit 一次。接着将 themes 文件夹移动回来,再 commit 一次。

\n
    \n
  1. \n

    删除或者先把 [Blogroot]/themes/butterfly/.git 移动到非博客文件夹目录下,原因是主题文件夹下的.git 文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。

    \n
  2. \n
  3. \n

    在博客根目录 [Blogroot] 路径下运行指令

    \n
    1
    2
    3
    4
    5
    git init #初始化
    git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为存放源码的github私有仓库
    git checkout -b master # 切换到master分支,
    #2020年10月后github新建仓库默认分支改为main,注意更改
    # 如果不是,后面的所有设置的分支记得保持一致
    \n
  4. \n
  5. \n

    添加屏蔽项
    \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。
    \n打开 [Blogroot]/.gitignore, 输入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .DS_Store
    Thumbs.db
    db.json
    *.log
    node_modules/
    public/
    .deploy*/
    .deploy_git*/
    .idea
    themes/butterfly/.git # 如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题
    \n
  6. \n
\n

完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署

\n
1
2
3
4
git add .
git commit -m "github action update"
git push origin master
#2020年10月后github新建仓库默认分支改为main,注意更改
\n

此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的.git 放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)

\n

查看部署情况

\n

\"image-20210913232006065\"

\n

点击正在运行的workflow就可以查看部署情况

\n

\"image-20210913233828678\"

\n

\"image-20210913234018254\"

\n

我遇到的Bug

\n

Bug:

\n

Spawn Failed

\n

原因主要就是配置deploy项时仓库的git链接没有正确配置,比如 :

\n
    \n
  1. gitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的)
  2. \n
  3. 配置coding时没有仔细看教程,用户名必须要使用生成 token 时系统同时生成的令牌用户名
  4. \n
\n

看板娘不能显示

\n

删除 BlogRoot/themes/butterfly/source/live2d-widget 下的 .git 文件

\n

然后执行:

\n
1
2
3
4
git rm --cached /themes/butterfly/source/live2d-widget
git add .
git commit -m "修复看板娘不能正常显示"
git push origin master
\n"},{"title":"51单片机学习笔记(四)","description":"串行通信中的IIC总线工作原理和协议+EEPROM","abbrlink":"c27dd6ec","date":"2021-08-19T08:07:10.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n\n\n目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线\n\n- UART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。\n\n- 1-wire:即单线总线,又叫单总线(只有一条线)。\n\n- I2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。\n\n- SPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。\n\n\n\n## 原理\n\nIIC串行总线,只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL\n\n如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据\n\n![image-20210822161125324](https://gitee.com/ajream/images/raw/master/img/20210822161128_image-20210822161125324.png)\n\n在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。\n\n在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况\n\n\n\n总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,\n\n即`SDA = SDA1 & SDA2 & SDA3 & ...`, `SCL=SCL1 & SCL2 & SCL3 & ...`\n\n![image-20210822161725608](https://gitee.com/ajream/images/raw/master/img/20210822161726_image-20210822161725608.png)\n\n\n\n\n\n### 起始信号和终止信号\n\nSCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;\n\nSCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。\n\n![image-20210822162305371](https://gitee.com/ajream/images/raw/master/img/20210822162306_image-20210822162305371.png)\n\n### 数据位有效性\n\nSCL为高电平期间,数据线上的数据必须保持稳定\n\n只有SCL信号为低电平期间,SDA状态才允许变化。\n\n![image-20210822162522027](https://gitee.com/ajream/images/raw/master/img/20210822162523_image-20210822162522027.png)\n\n### IIC总线的传送与应答\n\n每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节**最后面**都必须跟随一位应答位(即一帧共有9位)\n\n主机通过从机发出的**应答位**来判断从机是否成功接收数据:\n\n- 从机正忙于其他事情,发出应答1,表示没有收到\n- 当从机空闲可以接收该字节数据时,从机会发出应答0\n\n当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。\n\n\n\n### 数据帧格式\n\n在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用\"0\"表示主机发送数据(T),\"1\"表示主机接收数据(R)。\n\n每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。\n\n### 软件模拟IIC总线传送数据\n\n主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。\n\n为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :\n\n![image-20210822163923932](https://gitee.com/ajream/images/raw/master/img/20210822163925_image-20210822163923932.png)\n\n模拟的代码如下:\n\n```c\n//起始信号\nvoid I2cStart(){\n\tSCL = 1;\n\tSDA = 1;\n\tdelay5us(); //SDA在拉低低之前,应该先保持高电平时间>4us,这里用5us\n\tSDA = 0;\n\tdelay5us();\t //SDA保持低电平时间>4us,这里用5us\n}\n\n//终止信号\nvoid I2cStop(){\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();\n\tSDA = 1;\n\tdelay5us();\n}\n\n```\n\n第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下\n\n\n```c\n//主机读取从机的应答\nbit ReadACK(){\n\tSCL = 1;\n\tdelay5us();\n\tif(SDA){\n\t\tSCL = 0;\n\t\treturn(1);\n\t}\n\telse{\n\t\tSCL = 0;\n\t\treturn(0);\n\t}\n}\n\n\n```\n\n\n\n发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线\n\n```c\n//主机发送应答\nvoid SendACK(bit i){\n\tSCL = 0;\n \n\tif(i) SDA = 1;\n\telse SDA = 0;\n \n\tSCL = 1;\n\tdelay5us();\n\tSCL = 0; \t //拉低时钟总线\n\tSDA = 1;\t //释放数据总线\n}\n```\n\n\n\n## 串行EEPROM的扩展\n\nAteml公司的AT24C系列:\n\n- AT24C01:128字节(128×8位)\n\n- AT24C02:256字节(256×8位)\n- AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位)\n- AT24C16:2K字节(2K×8位)\n\n\n\n### 写入过程\n\n**获取地址码:**AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 `1010000`\n\n![image-20210824140820582](https://gitee.com/ajream/images/raw/master/img/20210824140829_image-20210824140820582.png)\n\n**写操作:**单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。\n\n\n\n**传送数据:**\n\n- 这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。\n\n- AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)\n\n- 当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:\n\n ![image-20210822171711924](https://gitee.com/ajream/images/raw/master/img/20210822171714_image-20210822171711924.png)\n \n > 说明:S为起始信号,然后是7位地址码+ `0`(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。\n \n- 注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。\n\n\n\n### 读出过程\n\n单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。 \n\n\n\n然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。\n\n当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。 \n\n![image-20210822171925336](https://gitee.com/ajream/images/raw/master/img/20210822171926_image-20210822171925336.png)\n\n\n\n完整代码:\n\n```c\n/*********************************************************************************\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tIIC通信,AT24C02读写数据,数码管显示数据。\t\t \t\t\t\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n#define At24c02ADDR 0XA0 //AT24C02硬件地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1;//I2C时钟总线\nsbit SDA = P2^0;//I2C数据总线\nuchar num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n/*====================================\n函数\t: delay(uint z)\n参数\t:z 延时毫秒设定,取值范围0-65535\n返回值\t:无\n描述\t:12T/Fosc11.0592M毫秒级延时\n====================================*/\nvoid delay(uint z){\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示数值,取值范围0-255\n返回值\t:无\n描述\t:三位共阴数码管动态显示\n====================================*/\nvoid display(uchar i){\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei){\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid timer0Init(){\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:delay5us()\n参数\t:无\n返回值\t:无\n描述\t:5us延时函数\n====================================*/\nvoid delay5us(){\n\t_nop_();\n}\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart(){\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop(){\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK(){\n\tSCL = 0;\t\t\t//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;\t\t\t//拉高,读SDA\n\tdelay5us();\n\tif(SDA){\t\t\t//NOACK\n\t\tSCL = 0;\n\t\treturn(1);\t\t//返回1\n\t}\n\telse{\t\t\t\t//ACK\n\t\tSCL = 0;\n\t\treturn(0);\t\t//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i){\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT){\n\tuchar i; \n\tfor(i=0; i<8; i++){ //分别写8次,每次写1位\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:At24c02Write(uchar ADDR, DAT)\n参数\t:ADDR 单元地址0-255,DAT 需要输入的数据0-255\n返回值\t:无\n描述\t:At24c02指定单元写入一个字节数据\n====================================*/\nvoid At24c02Write(uchar ADDR, DAT){\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK()) //读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//发送储存单元地址字节\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(DAT);//发送一字节数据\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cStop();\t//I2C停止信号\n}\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte(){\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++){ \t//分别读8次,每次读一位\n\t\n\t\tDAT <<= 1; \t\t\t//数据左移1位,准备接收一位\n\t\tSCL = 0; \t\t\t//拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; \t\t\t//拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;\t//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); \t\t\t//返回读出的数据\n}\n\n/*====================================\n函数\t:At24c02Read(uchar ADDR)\n参数\t:ADDR 单元地址\t0-255\n返回值\t:返回指定单元的数据\n描述\t:读AT24C02指定单元内数据\n====================================*/\nuchar At24c02Read(uchar ADDR){\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//I2C发送一个字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环{\t\n\ttimer0Init();//定时器0初始化\n\tEA = 0;//屏蔽中断\n\tAt24c02Write(3, 188);//给第3单元写入数据“188”\n\tdelay(1);//延时等待AT24C02处理\n\tnum = At24c02Read(3);//读出第3单元内数据送给显示变量\n\tif(AckFlag)//当从机非应答\n\t\tP1 = 0;//亮P1所有灯\n\telse\n\t\tP1 = 0XFF;//灭P1所有灯\n\tEA = 1;//开中断\n\twhile(1);\n} \n\n//定时器0中断函数\nvoid timer0() interrupt 1 {\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(四).md","raw":"---\ntitle: 51单片机学习笔记(四)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 串行通信中的IIC总线工作原理和协议+EEPROM\nabbrlink: c27dd6ec\ndate: 2021-08-19 16:07:10\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n\n\n目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线\n\n- UART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。\n\n- 1-wire:即单线总线,又叫单总线(只有一条线)。\n\n- I2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。\n\n- SPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。\n\n\n\n## 原理\n\nIIC串行总线,只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL\n\n如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据\n\n![image-20210822161125324](https://gitee.com/ajream/images/raw/master/img/20210822161128_image-20210822161125324.png)\n\n在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。\n\n在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况\n\n\n\n总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,\n\n即`SDA = SDA1 & SDA2 & SDA3 & ...`, `SCL=SCL1 & SCL2 & SCL3 & ...`\n\n![image-20210822161725608](https://gitee.com/ajream/images/raw/master/img/20210822161726_image-20210822161725608.png)\n\n\n\n\n\n### 起始信号和终止信号\n\nSCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;\n\nSCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。\n\n![image-20210822162305371](https://gitee.com/ajream/images/raw/master/img/20210822162306_image-20210822162305371.png)\n\n### 数据位有效性\n\nSCL为高电平期间,数据线上的数据必须保持稳定\n\n只有SCL信号为低电平期间,SDA状态才允许变化。\n\n![image-20210822162522027](https://gitee.com/ajream/images/raw/master/img/20210822162523_image-20210822162522027.png)\n\n### IIC总线的传送与应答\n\n每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节**最后面**都必须跟随一位应答位(即一帧共有9位)\n\n主机通过从机发出的**应答位**来判断从机是否成功接收数据:\n\n- 从机正忙于其他事情,发出应答1,表示没有收到\n- 当从机空闲可以接收该字节数据时,从机会发出应答0\n\n当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。\n\n\n\n### 数据帧格式\n\n在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用\"0\"表示主机发送数据(T),\"1\"表示主机接收数据(R)。\n\n每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。\n\n### 软件模拟IIC总线传送数据\n\n主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。\n\n为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :\n\n![image-20210822163923932](https://gitee.com/ajream/images/raw/master/img/20210822163925_image-20210822163923932.png)\n\n模拟的代码如下:\n\n```c\n//起始信号\nvoid I2cStart(){\n\tSCL = 1;\n\tSDA = 1;\n\tdelay5us(); //SDA在拉低低之前,应该先保持高电平时间>4us,这里用5us\n\tSDA = 0;\n\tdelay5us();\t //SDA保持低电平时间>4us,这里用5us\n}\n\n//终止信号\nvoid I2cStop(){\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();\n\tSDA = 1;\n\tdelay5us();\n}\n\n```\n\n第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下\n\n\n```c\n//主机读取从机的应答\nbit ReadACK(){\n\tSCL = 1;\n\tdelay5us();\n\tif(SDA){\n\t\tSCL = 0;\n\t\treturn(1);\n\t}\n\telse{\n\t\tSCL = 0;\n\t\treturn(0);\n\t}\n}\n\n\n```\n\n\n\n发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线\n\n```c\n//主机发送应答\nvoid SendACK(bit i){\n\tSCL = 0;\n \n\tif(i) SDA = 1;\n\telse SDA = 0;\n \n\tSCL = 1;\n\tdelay5us();\n\tSCL = 0; \t //拉低时钟总线\n\tSDA = 1;\t //释放数据总线\n}\n```\n\n\n\n## 串行EEPROM的扩展\n\nAteml公司的AT24C系列:\n\n- AT24C01:128字节(128×8位)\n\n- AT24C02:256字节(256×8位)\n- AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位)\n- AT24C16:2K字节(2K×8位)\n\n\n\n### 写入过程\n\n**获取地址码:**AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 `1010000`\n\n![image-20210824140820582](https://gitee.com/ajream/images/raw/master/img/20210824140829_image-20210824140820582.png)\n\n**写操作:**单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。\n\n\n\n**传送数据:**\n\n- 这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。\n\n- AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)\n\n- 当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:\n\n ![image-20210822171711924](https://gitee.com/ajream/images/raw/master/img/20210822171714_image-20210822171711924.png)\n \n > 说明:S为起始信号,然后是7位地址码+ `0`(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。\n \n- 注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。\n\n\n\n### 读出过程\n\n单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。 \n\n\n\n然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。\n\n当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。 \n\n![image-20210822171925336](https://gitee.com/ajream/images/raw/master/img/20210822171926_image-20210822171925336.png)\n\n\n\n完整代码:\n\n```c\n/*********************************************************************************\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tIIC通信,AT24C02读写数据,数码管显示数据。\t\t \t\t\t\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n#define At24c02ADDR 0XA0 //AT24C02硬件地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1;//I2C时钟总线\nsbit SDA = P2^0;//I2C数据总线\nuchar num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n/*====================================\n函数\t: delay(uint z)\n参数\t:z 延时毫秒设定,取值范围0-65535\n返回值\t:无\n描述\t:12T/Fosc11.0592M毫秒级延时\n====================================*/\nvoid delay(uint z){\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示数值,取值范围0-255\n返回值\t:无\n描述\t:三位共阴数码管动态显示\n====================================*/\nvoid display(uchar i){\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei){\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid timer0Init(){\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:delay5us()\n参数\t:无\n返回值\t:无\n描述\t:5us延时函数\n====================================*/\nvoid delay5us(){\n\t_nop_();\n}\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart(){\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop(){\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK(){\n\tSCL = 0;\t\t\t//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;\t\t\t//拉高,读SDA\n\tdelay5us();\n\tif(SDA){\t\t\t//NOACK\n\t\tSCL = 0;\n\t\treturn(1);\t\t//返回1\n\t}\n\telse{\t\t\t\t//ACK\n\t\tSCL = 0;\n\t\treturn(0);\t\t//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i){\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT){\n\tuchar i; \n\tfor(i=0; i<8; i++){ //分别写8次,每次写1位\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:At24c02Write(uchar ADDR, DAT)\n参数\t:ADDR 单元地址0-255,DAT 需要输入的数据0-255\n返回值\t:无\n描述\t:At24c02指定单元写入一个字节数据\n====================================*/\nvoid At24c02Write(uchar ADDR, DAT){\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK()) //读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//发送储存单元地址字节\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(DAT);//发送一字节数据\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cStop();\t//I2C停止信号\n}\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte(){\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++){ \t//分别读8次,每次读一位\n\t\n\t\tDAT <<= 1; \t\t\t//数据左移1位,准备接收一位\n\t\tSCL = 0; \t\t\t//拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; \t\t\t//拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;\t//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); \t\t\t//返回读出的数据\n}\n\n/*====================================\n函数\t:At24c02Read(uchar ADDR)\n参数\t:ADDR 单元地址\t0-255\n返回值\t:返回指定单元的数据\n描述\t:读AT24C02指定单元内数据\n====================================*/\nuchar At24c02Read(uchar ADDR){\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//I2C发送一个字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环{\t\n\ttimer0Init();//定时器0初始化\n\tEA = 0;//屏蔽中断\n\tAt24c02Write(3, 188);//给第3单元写入数据“188”\n\tdelay(1);//延时等待AT24C02处理\n\tnum = At24c02Read(3);//读出第3单元内数据送给显示变量\n\tif(AckFlag)//当从机非应答\n\t\tP1 = 0;//亮P1所有灯\n\telse\n\t\tP1 = 0XFF;//灭P1所有灯\n\tEA = 1;//开中断\n\twhile(1);\n} \n\n//定时器0中断函数\nvoid timer0() interrupt 1 {\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(四)","published":1,"updated":"2021-09-10T12:45:36.194Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6og000vakve4p1e67aw","content":"

目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线

\n
    \n
  • UART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。

    \n
  • \n
  • 1-wire:即单线总线,又叫单总线(只有一条线)。

    \n
  • \n
  • I2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。

    \n
  • \n
  • SPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。

    \n
  • \n
\n

原理

IIC串行总线,只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL

\n

如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据

\n

\"image-20210822161125324\"

\n

在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。

\n

在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况

\n

总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,

\n

SDA = SDA1 & SDA2 & SDA3 & ..., SCL=SCL1 & SCL2 & SCL3 & ...

\n

\"image-20210822161725608\"

\n

起始信号和终止信号

SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;

\n

SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。

\n

\"image-20210822162305371\"

\n

数据位有效性

SCL为高电平期间,数据线上的数据必须保持稳定

\n

只有SCL信号为低电平期间,SDA状态才允许变化。

\n

\"image-20210822162522027\"

\n

IIC总线的传送与应答

每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节最后面都必须跟随一位应答位(即一帧共有9位)

\n

主机通过从机发出的应答位来判断从机是否成功接收数据:

\n
    \n
  • 从机正忙于其他事情,发出应答1,表示没有收到
  • \n
  • 当从机空闲可以接收该字节数据时,从机会发出应答0
  • \n
\n

当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。

\n

数据帧格式

在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用”0”表示主机发送数据(T),”1”表示主机接收数据(R)。

\n

每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。

\n

软件模拟IIC总线传送数据

主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。

\n

为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :

\n

\"image-20210822163923932\"

\n

模拟的代码如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//起始信号
void I2cStart(){
\tSCL = 1;
\tSDA = 1;
\tdelay5us(); //SDA在拉低低之前,应该先保持高电平时间>4us,这里用5us
\tSDA = 0;
\tdelay5us();\t //SDA保持低电平时间>4us,这里用5us
}

//终止信号
void I2cStop(){
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();
\tSDA = 1;
\tdelay5us();
}

\n

第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//主机读取从机的应答
bit ReadACK(){
\tSCL = 1;
\tdelay5us();
\tif(SDA){
\t\tSCL = 0;
\t\treturn(1);
\t}
\telse{
\t\tSCL = 0;
\t\treturn(0);
\t}
}


\n

发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线

\n
1
2
3
4
5
6
7
8
9
10
11
12
//主机发送应答
void SendACK(bit i){
\tSCL = 0;

\tif(i) SDA = 1;
\telse SDA = 0;

\tSCL = 1;
\tdelay5us();
\tSCL = 0; \t //拉低时钟总线
\tSDA = 1;\t //释放数据总线
}
\n

串行EEPROM的扩展

Ateml公司的AT24C系列:

\n
    \n
  • AT24C01:128字节(128×8位)

    \n
  • \n
  • AT24C02:256字节(256×8位)

    \n
  • \n
  • AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位)
  • \n
  • AT24C16:2K字节(2K×8位)
  • \n
\n

写入过程

获取地址码:AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 1010000

\n

\"image-20210824140820582\"

\n

写操作:单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。

\n

传送数据:

\n
    \n
  • 这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。

    \n
  • \n
  • AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)

    \n
  • \n
  • 当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:

    \n

    \"image-20210822171711924\"

    \n
    \n

    说明:S为起始信号,然后是7位地址码+ 0(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。

    \n
    \n
  • \n
  • 注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。

    \n
  • \n
\n

读出过程

单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。

\n

然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。

\n

当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。

\n

\"image-20210822171925336\"

\n

完整代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/*********************************************************************************
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tIIC通信,AT24C02读写数据,数码管显示数据。\t\t \t\t\t
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char
#define At24c02ADDR 0XA0 //AT24C02硬件地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1;//I2C时钟总线
sbit SDA = P2^0;//I2C数据总线
uchar num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

/*====================================
函数\t: delay(uint z)
参数\t:z 延时毫秒设定,取值范围0-65535
返回值\t:无
描述\t:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z){
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示数值,取值范围0-255
返回值\t:无
描述\t:三位共阴数码管动态显示
====================================*/
void display(uchar i){
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei){
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void timer0Init(){
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}
/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:delay5us()
参数\t:无
返回值\t:无
描述\t:5us延时函数
====================================*/
void delay5us(){
\t_nop_();
}

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart(){
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop(){
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK(){
\tSCL = 0;\t\t\t//拉低时钟总线,允许从机控制SDA
\tSCL = 1;\t\t\t//拉高,读SDA
\tdelay5us();
\tif(SDA){\t\t\t//NOACK
\t\tSCL = 0;
\t\treturn(1);\t\t//返回1
\t}
\telse{\t\t\t\t//ACK
\t\tSCL = 0;
\t\treturn(0);\t\t//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i){
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT){
\tuchar i;
\tfor(i=0; i<8; i++){ //分别写8次,每次写1位
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:At24c02Write(uchar ADDR, DAT)
参数\t:ADDR 单元地址0-255,DAT 需要输入的数据0-255
返回值\t:无
描述\t:At24c02指定单元写入一个字节数据
====================================*/
void At24c02Write(uchar ADDR, DAT){
\tI2cStart();//I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK()) //读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(ADDR);//发送储存单元地址字节
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(DAT);//发送一字节数据
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cStop();\t//I2C停止信号
}

/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte(){
\tuchar i, DAT;
\tfor(i=0; i<8; i++){ \t//分别读8次,每次读一位
\t
\t\tDAT <<= 1; \t\t\t//数据左移1位,准备接收一位
\t\tSCL = 0; \t\t\t//拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; \t\t\t//拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;\t//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); \t\t\t//返回读出的数据
}

/*====================================
函数\t:At24c02Read(uchar ADDR)
参数\t:ADDR 单元地址\t0-255
返回值\t:返回指定单元的数据
描述\t:读AT24C02指定单元内数据
====================================*/
uchar At24c02Read(uchar ADDR){
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(ADDR);//I2C发送一个字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环{\t
\ttimer0Init();//定时器0初始化
\tEA = 0;//屏蔽中断
\tAt24c02Write(3, 188);//给第3单元写入数据“188”
\tdelay(1);//延时等待AT24C02处理
\tnum = At24c02Read(3);//读出第3单元内数据送给显示变量
\tif(AckFlag)//当从机非应答
\t\tP1 = 0;//亮P1所有灯
\telse
\t\tP1 = 0XFF;//灭P1所有灯
\tEA = 1;//开中断
\twhile(1);
}

//定时器0中断函数
void timer0() interrupt 1 {
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线

\n
    \n
  • UART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。

    \n
  • \n
  • 1-wire:即单线总线,又叫单总线(只有一条线)。

    \n
  • \n
  • I2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。

    \n
  • \n
  • SPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。

    \n
  • \n
\n

原理

IIC串行总线,只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL

\n

如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据

\n

\"image-20210822161125324\"

\n

在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。

\n

在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况

\n

总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,

\n

SDA = SDA1 & SDA2 & SDA3 & ..., SCL=SCL1 & SCL2 & SCL3 & ...

\n

\"image-20210822161725608\"

\n

起始信号和终止信号

SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;

\n

SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。

\n

\"image-20210822162305371\"

\n

数据位有效性

SCL为高电平期间,数据线上的数据必须保持稳定

\n

只有SCL信号为低电平期间,SDA状态才允许变化。

\n

\"image-20210822162522027\"

\n

IIC总线的传送与应答

每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节最后面都必须跟随一位应答位(即一帧共有9位)

\n

主机通过从机发出的应答位来判断从机是否成功接收数据:

\n
    \n
  • 从机正忙于其他事情,发出应答1,表示没有收到
  • \n
  • 当从机空闲可以接收该字节数据时,从机会发出应答0
  • \n
\n

当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。

\n

数据帧格式

在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用”0”表示主机发送数据(T),”1”表示主机接收数据(R)。

\n

每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。

\n

软件模拟IIC总线传送数据

主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。

\n

为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :

\n

\"image-20210822163923932\"

\n

模拟的代码如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//起始信号
void I2cStart(){
\tSCL = 1;
\tSDA = 1;
\tdelay5us(); //SDA在拉低低之前,应该先保持高电平时间>4us,这里用5us
\tSDA = 0;
\tdelay5us();\t //SDA保持低电平时间>4us,这里用5us
}

//终止信号
void I2cStop(){
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();
\tSDA = 1;
\tdelay5us();
}

\n

第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//主机读取从机的应答
bit ReadACK(){
\tSCL = 1;
\tdelay5us();
\tif(SDA){
\t\tSCL = 0;
\t\treturn(1);
\t}
\telse{
\t\tSCL = 0;
\t\treturn(0);
\t}
}


\n

发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线

\n
1
2
3
4
5
6
7
8
9
10
11
12
//主机发送应答
void SendACK(bit i){
\tSCL = 0;

\tif(i) SDA = 1;
\telse SDA = 0;

\tSCL = 1;
\tdelay5us();
\tSCL = 0; \t //拉低时钟总线
\tSDA = 1;\t //释放数据总线
}
\n

串行EEPROM的扩展

Ateml公司的AT24C系列:

\n
    \n
  • AT24C01:128字节(128×8位)

    \n
  • \n
  • AT24C02:256字节(256×8位)

    \n
  • \n
  • AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位)
  • \n
  • AT24C16:2K字节(2K×8位)
  • \n
\n

写入过程

获取地址码:AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 1010000

\n

\"image-20210824140820582\"

\n

写操作:单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。

\n

传送数据:

\n
    \n
  • 这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。

    \n
  • \n
  • AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)

    \n
  • \n
  • 当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:

    \n

    \"image-20210822171711924\"

    \n
    \n

    说明:S为起始信号,然后是7位地址码+ 0(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。

    \n
    \n
  • \n
  • 注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。

    \n
  • \n
\n

读出过程

单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。

\n

然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。

\n

当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。

\n

\"image-20210822171925336\"

\n

完整代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/*********************************************************************************
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tIIC通信,AT24C02读写数据,数码管显示数据。\t\t \t\t\t
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char
#define At24c02ADDR 0XA0 //AT24C02硬件地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1;//I2C时钟总线
sbit SDA = P2^0;//I2C数据总线
uchar num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

/*====================================
函数\t: delay(uint z)
参数\t:z 延时毫秒设定,取值范围0-65535
返回值\t:无
描述\t:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z){
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示数值,取值范围0-255
返回值\t:无
描述\t:三位共阴数码管动态显示
====================================*/
void display(uchar i){
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei){
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void timer0Init(){
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}
/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:delay5us()
参数\t:无
返回值\t:无
描述\t:5us延时函数
====================================*/
void delay5us(){
\t_nop_();
}

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart(){
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop(){
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK(){
\tSCL = 0;\t\t\t//拉低时钟总线,允许从机控制SDA
\tSCL = 1;\t\t\t//拉高,读SDA
\tdelay5us();
\tif(SDA){\t\t\t//NOACK
\t\tSCL = 0;
\t\treturn(1);\t\t//返回1
\t}
\telse{\t\t\t\t//ACK
\t\tSCL = 0;
\t\treturn(0);\t\t//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i){
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT){
\tuchar i;
\tfor(i=0; i<8; i++){ //分别写8次,每次写1位
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:At24c02Write(uchar ADDR, DAT)
参数\t:ADDR 单元地址0-255,DAT 需要输入的数据0-255
返回值\t:无
描述\t:At24c02指定单元写入一个字节数据
====================================*/
void At24c02Write(uchar ADDR, DAT){
\tI2cStart();//I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK()) //读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(ADDR);//发送储存单元地址字节
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(DAT);//发送一字节数据
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cStop();\t//I2C停止信号
}

/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte(){
\tuchar i, DAT;
\tfor(i=0; i<8; i++){ \t//分别读8次,每次读一位
\t
\t\tDAT <<= 1; \t\t\t//数据左移1位,准备接收一位
\t\tSCL = 0; \t\t\t//拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; \t\t\t//拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;\t//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); \t\t\t//返回读出的数据
}

/*====================================
函数\t:At24c02Read(uchar ADDR)
参数\t:ADDR 单元地址\t0-255
返回值\t:返回指定单元的数据
描述\t:读AT24C02指定单元内数据
====================================*/
uchar At24c02Read(uchar ADDR){
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(ADDR);//I2C发送一个字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环{\t
\ttimer0Init();//定时器0初始化
\tEA = 0;//屏蔽中断
\tAt24c02Write(3, 188);//给第3单元写入数据“188”
\tdelay(1);//延时等待AT24C02处理
\tnum = At24c02Read(3);//读出第3单元内数据送给显示变量
\tif(AckFlag)//当从机非应答
\t\tP1 = 0;//亮P1所有灯
\telse
\t\tP1 = 0XFF;//灭P1所有灯
\tEA = 1;//开中断
\twhile(1);
}

//定时器0中断函数
void timer0() interrupt 1 {
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}
\n"},{"title":"java垃圾回收算法","abbrlink":"bdfc8ea1","date":"2021-05-16T03:16:42.000Z","description":"Java垃圾回收原理解析","_content":"\n\n\n# Java垃圾回收算法\n\n[咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/73628158)\n\n## 什么是垃圾(定义)\n\n有两种算法来定义\n\n### 引用计数算法\n\n引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。\n\n> **缺点:**循环引用(其计数永远不会为0)\n\n### 可达性分析算法\n\n可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。\n\n\"image-20210423190611076\"\n\n\n\n> 可达性算法,解决了引用计数所无法解决的问题-“循环依赖”\n\n## 怎么回收(算法)\n\n### 标记-清除算法\n\n\"image-20210424225646255\"\n\n标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;\n\n缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存\n\n\n\n### 复制算法\n\n**回收前:**\n\n\"image-20210424231518421\"\n\n**回收后:**\n\n\"image-20210424231836708\"\n\n\n\n原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:\n\n1. 首先把存活的对象都复制到另一块空内存上\n2. 再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。\n\n优点:解决了标记-清除算法的问题\n\n缺点:同一时间只能使用其中一半的内存\n\n### 标记-整理算法\n\n\"img\"\n\n标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。\n\n优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。\n\n缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。\n\n\n\n### 分代收集算法\n\n分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。\n\n对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。\n\n1. 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。\n2. 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记-整理算法来进行回收。\n\n\n\n## 内存模型与回收策略\n\n内存模型:\n\n\"img\"\n\n\n\n可以看到,内存模型分为 :\n\n- Eden\n\n- Survior\n\n- Old\n\n 这3个区,而Survior又分为 From、to两个区\n\n### Eden区\n\nIBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC **更频繁,回收速度也更快**。\n\n> 通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。\n\n### Survivor区\n\nSurvivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。\n\n1、为啥需要Survivor区?\n\n不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。\n\n> 所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。\n\n2、为啥需要俩?\n\n设置两个 Survivor 区最大的好处就是解决内存碎片化。\n\n我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们**只能标记清除**,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。\n\n这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。\n\n### Old 区\n\n老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 --- 整理算法。\n\n除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。\n\n1、大对象\n\n大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。\n\n2、长期存活对象\n\n虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。\n\n3、动态对象年龄\n\n虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。\n\n这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。\n\n\n\n","source":"_posts/Java面试/Java垃圾回收算法.md","raw":"---\ntitle: java垃圾回收算法\ntags:\n - Java面试\ncategories:\n - java\n - java面试\nabbrlink: bdfc8ea1\ndate: 2021-05-16 11:16:42\ndescription: Java垃圾回收原理解析\n---\n\n\n\n# Java垃圾回收算法\n\n[咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/73628158)\n\n## 什么是垃圾(定义)\n\n有两种算法来定义\n\n### 引用计数算法\n\n引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。\n\n> **缺点:**循环引用(其计数永远不会为0)\n\n### 可达性分析算法\n\n可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。\n\n\"image-20210423190611076\"\n\n\n\n> 可达性算法,解决了引用计数所无法解决的问题-“循环依赖”\n\n## 怎么回收(算法)\n\n### 标记-清除算法\n\n\"image-20210424225646255\"\n\n标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;\n\n缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存\n\n\n\n### 复制算法\n\n**回收前:**\n\n\"image-20210424231518421\"\n\n**回收后:**\n\n\"image-20210424231836708\"\n\n\n\n原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:\n\n1. 首先把存活的对象都复制到另一块空内存上\n2. 再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。\n\n优点:解决了标记-清除算法的问题\n\n缺点:同一时间只能使用其中一半的内存\n\n### 标记-整理算法\n\n\"img\"\n\n标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。\n\n优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。\n\n缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。\n\n\n\n### 分代收集算法\n\n分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。\n\n对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。\n\n1. 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。\n2. 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记-整理算法来进行回收。\n\n\n\n## 内存模型与回收策略\n\n内存模型:\n\n\"img\"\n\n\n\n可以看到,内存模型分为 :\n\n- Eden\n\n- Survior\n\n- Old\n\n 这3个区,而Survior又分为 From、to两个区\n\n### Eden区\n\nIBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC **更频繁,回收速度也更快**。\n\n> 通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。\n\n### Survivor区\n\nSurvivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。\n\n1、为啥需要Survivor区?\n\n不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。\n\n> 所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。\n\n2、为啥需要俩?\n\n设置两个 Survivor 区最大的好处就是解决内存碎片化。\n\n我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们**只能标记清除**,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。\n\n这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。\n\n### Old 区\n\n老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 --- 整理算法。\n\n除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。\n\n1、大对象\n\n大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。\n\n2、长期存活对象\n\n虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。\n\n3、动态对象年龄\n\n虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。\n\n这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。\n\n\n\n","slug":"Java面试/Java垃圾回收算法","published":1,"updated":"2021-09-11T08:01:37.796Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6og000xakve4mebdy6p","content":"

Java垃圾回收算法

咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)

\n

什么是垃圾(定义)

有两种算法来定义

\n

引用计数算法

引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。

\n
\n

缺点:循环引用(其计数永远不会为0)

\n
\n

可达性分析算法

可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。

\n

\"image-20210423190611076\"

\n
\n

可达性算法,解决了引用计数所无法解决的问题-“循环依赖”

\n
\n

怎么回收(算法)

标记-清除算法

\"image-20210424225646255\"

\n

标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;

\n

缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存

\n

复制算法

回收前:

\n

\"image-20210424231518421\"

\n

回收后:

\n

\"image-20210424231836708\"

\n

原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:

\n
    \n
  1. 首先把存活的对象都复制到另一块空内存上
  2. \n
  3. 再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。
  4. \n
\n

优点:解决了标记-清除算法的问题

\n

缺点:同一时间只能使用其中一半的内存

\n

标记-整理算法

\"img\"

\n

标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。

\n

优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。

\n

缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。

\n

分代收集算法

分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。

\n

对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

\n
    \n
  1. 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
  2. \n
  3. 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记-整理算法来进行回收。
  4. \n
\n

内存模型与回收策略

内存模型:

\n

\"img\"

\n

可以看到,内存模型分为 :

\n
    \n
  • Eden

    \n
  • \n
  • Survior

    \n
  • \n
  • Old

    \n

    这3个区,而Survior又分为 From、to两个区

    \n
  • \n
\n

Eden区

IBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC 更频繁,回收速度也更快

\n
\n

通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。

\n
\n

Survivor区

Survivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。

\n

1、为啥需要Survivor区?

\n

不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。

\n
\n

所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。

\n
\n

2、为啥需要俩?

\n

设置两个 Survivor 区最大的好处就是解决内存碎片化。

\n

我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们只能标记清除,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。

\n

这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。

\n

Old 区

老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 —- 整理算法。

\n

除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。

\n

1、大对象

\n

大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。

\n

2、长期存活对象

\n

虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。

\n

3、动态对象年龄

\n

虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。

\n

这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

Java垃圾回收算法

咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)

\n

什么是垃圾(定义)

有两种算法来定义

\n

引用计数算法

引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。

\n
\n

缺点:循环引用(其计数永远不会为0)

\n
\n

可达性分析算法

可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。

\n

\"image-20210423190611076\"

\n
\n

可达性算法,解决了引用计数所无法解决的问题-“循环依赖”

\n
\n

怎么回收(算法)

标记-清除算法

\"image-20210424225646255\"

\n

标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;

\n

缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存

\n

复制算法

回收前:

\n

\"image-20210424231518421\"

\n

回收后:

\n

\"image-20210424231836708\"

\n

原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:

\n
    \n
  1. 首先把存活的对象都复制到另一块空内存上
  2. \n
  3. 再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。
  4. \n
\n

优点:解决了标记-清除算法的问题

\n

缺点:同一时间只能使用其中一半的内存

\n

标记-整理算法

\"img\"

\n

标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。

\n

优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。

\n

缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。

\n

分代收集算法

分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。

\n

对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

\n
    \n
  1. 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
  2. \n
  3. 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记-整理算法来进行回收。
  4. \n
\n

内存模型与回收策略

内存模型:

\n

\"img\"

\n

可以看到,内存模型分为 :

\n
    \n
  • Eden

    \n
  • \n
  • Survior

    \n
  • \n
  • Old

    \n

    这3个区,而Survior又分为 From、to两个区

    \n
  • \n
\n

Eden区

IBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC 更频繁,回收速度也更快

\n
\n

通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。

\n
\n

Survivor区

Survivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。

\n

1、为啥需要Survivor区?

\n

不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。

\n
\n

所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。

\n
\n

2、为啥需要俩?

\n

设置两个 Survivor 区最大的好处就是解决内存碎片化。

\n

我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们只能标记清除,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。

\n

这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。

\n

Old 区

老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 —- 整理算法。

\n

除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。

\n

1、大对象

\n

大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。

\n

2、长期存活对象

\n

虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。

\n

3、动态对象年龄

\n

虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。

\n

这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。

\n"},{"title":"标签外挂","date":"2021-09-08T12:13:46.000Z","abbrlink":"54667693","description":"Hexo博客使用标签外挂","cover":"https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png","_content":"\n## 标签外挂\n\n### note\n{% tabs Tabs %}\n\n```markdown\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n**预览**\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n```markdown\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n预览\n\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n\n\n```markdown\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n```\n\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n\n\n\n### Tab\n```markdown\n{% tabs Unique name, [index] %}\n\nAny content (support inline tags too).\n\n{% endtabs %}\n\nUnique name : Unique name of tabs block tag without comma.\n Will be used in #id's as prefix for each tab with their index numbers.\n If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.\n Only for current url of post/page must be unique!\n[index] : Index number of active tab.\n If not specified, first tab (1) will be selected.\n If index is -1, no tab will be selected. It's will be something like spoiler.\n Optional parameter.\n[Tab caption] : Caption of current tab.\n If not caption specified, unique name with tab index suffix will be used as caption of tab.\n If not caption specified, but specified icon, caption will empty.\n Optional parameter.\n[@icon] : FontAwesome icon name (full-name, look like 'fas fa-font')\n Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'.\n Optional parameter.\n\n```\n\n**方案一:默认选择第一个tab**\n\n```markdown\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n\n**方案二:默认选择第3个tab**\n\n```markdown\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案三:没有预设值**\n\n```markdown\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案四:自定义每个tab的name和图标icon**\n\n```markdown\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n```\n\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n### folding\n\n```\n{% folding 参数(可选), 标题 %}\n\n内容\n\n{% endfolding %}\n\n```\n\n{%tabs t2 -1%}\n\n\n参数位置可以填写颜色和状态,多个参数用空格隔开。\n\n- 颜色\n\nblue, cyan, green, yellow, red\n\n- 状态\n\n状态填写 open 代表默认打开\n\n\n\n\n```markdown\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n```\n\n\n\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n\n{% endtabs %}","source":"_posts/Hexo/标签外挂.md","raw":"---\ntitle: 标签外挂\ndate: '2021-09-08 20:13:46'\nabbrlink: '54667693'\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: Hexo博客使用标签外挂\ncover: https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png\n---\n\n## 标签外挂\n\n### note\n{% tabs Tabs %}\n\n```markdown\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n**预览**\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n```markdown\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n预览\n\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n\n\n```markdown\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n```\n\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n\n\n\n### Tab\n```markdown\n{% tabs Unique name, [index] %}\n\nAny content (support inline tags too).\n\n{% endtabs %}\n\nUnique name : Unique name of tabs block tag without comma.\n Will be used in #id's as prefix for each tab with their index numbers.\n If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.\n Only for current url of post/page must be unique!\n[index] : Index number of active tab.\n If not specified, first tab (1) will be selected.\n If index is -1, no tab will be selected. It's will be something like spoiler.\n Optional parameter.\n[Tab caption] : Caption of current tab.\n If not caption specified, unique name with tab index suffix will be used as caption of tab.\n If not caption specified, but specified icon, caption will empty.\n Optional parameter.\n[@icon] : FontAwesome icon name (full-name, look like 'fas fa-font')\n Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'.\n Optional parameter.\n\n```\n\n**方案一:默认选择第一个tab**\n\n```markdown\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n\n**方案二:默认选择第3个tab**\n\n```markdown\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案三:没有预设值**\n\n```markdown\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案四:自定义每个tab的name和图标icon**\n\n```markdown\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n```\n\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n### folding\n\n```\n{% folding 参数(可选), 标题 %}\n\n内容\n\n{% endfolding %}\n\n```\n\n{%tabs t2 -1%}\n\n\n参数位置可以填写颜色和状态,多个参数用空格隔开。\n\n- 颜色\n\nblue, cyan, green, yellow, red\n\n- 状态\n\n状态填写 open 代表默认打开\n\n\n\n\n```markdown\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n```\n\n\n\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n\n{% endtabs %}","slug":"Hexo/标签外挂","published":1,"updated":"2021-09-16T15:28:42.688Z","_id":"cktk8o6oi0011akve2ujw438x","comments":1,"layout":"post","photos":[],"link":"","content":"

标签外挂

\n

note

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note simple %}
默认 提示块标籤
{% endnote %}

{% note default simple %}
default 提示块标籤
{% endnote %}

{% note primary simple %}
primary 提示块标籤
{% endnote %}

{% note success simple %}
success 提示块标籤
{% endnote %}

{% note info simple %}
info 提示块标籤
{% endnote %}

{% note warning simple %}
warning 提示块标籤
{% endnote %}

{% note danger simple %}
danger 提示块标籤
{% endnote %}

\n

预览

\n

默认 提示块标籤

\n
\n

default 提示块标籤

\n
\n

primary 提示块标籤

\n
\n

success 提示块标籤

\n
\n

info 提示块标籤

\n
\n

warning 提示块标籤

\n
\n

danger 提示块标籤

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note modern %}
默认 提示块标籤
{% endnote %}

{% note default modern %}
default 提示块标籤
{% endnote %}

{% note primary modern %}
primary 提示块标籤
{% endnote %}

{% note success modern %}
success 提示块标籤
{% endnote %}

{% note info modern %}
info 提示块标籤
{% endnote %}

{% note warning modern %}
warning 提示块标籤
{% endnote %}

{% note danger modern %}
danger 提示块标籤
{% endnote %}

\n

预览

\n

默认 提示块标籤

\n
\n

default 提示块标籤

\n
\n

primary 提示块标籤

\n
\n

success 提示块标籤

\n
\n

info 提示块标籤

\n
\n

warning 提示块标籤

\n
\n

danger 提示块标籤

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note flat %}
默认 提示块标签
{% endnote %}

{% note default flat %}
default 提示块标签
{% endnote %}

{% note primary flat %}
primary 提示块标签
{% endnote %}

{% note success flat %}
success 提示块标签
{% endnote %}

{% note info flat %}
info 提示块标签
{% endnote %}

{% note warning flat %}
warning 提示块标签
{% endnote %}

{% note danger flat %}
danger 提示块标签
{% endnote %}

\n

默认 提示块标签

\n
\n

default 提示块标签

\n
\n

primary 提示块标签

\n
\n

success 提示块标签

\n
\n

info 提示块标签

\n
\n

warning 提示块标签

\n
\n

danger 提示块标签

\n
\n

Tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{% tabs Unique name, [index] %}
<!-- tab [Tab caption] [@icon] -->
Any content (support inline tags too).
<!-- endtab -->
{% endtabs %}

Unique name : Unique name of tabs block tag without comma.
Will be used in #id's as prefix for each tab with their index numbers.
If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.
Only for current url of post/page must be unique!
[index] : Index number of active tab.
If not specified, first tab (1) will be selected.
If index is -1, no tab will be selected. It's will be something like spoiler.
Optional parameter.
[Tab caption] : Caption of current tab.
If not caption specified, unique name with tab index suffix will be used as caption of tab.
If not caption specified, but specified icon, caption will empty.
Optional parameter.
[@icon] : FontAwesome icon name (full-name, look like 'fas fa-font')
Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'.
Optional parameter.

\n

方案一:默认选择第一个tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test1 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案二:默认选择第3个tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test2, 3 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案三:没有预设值

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test3, -1 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案四:自定义每个tab的name和图标icon

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test4 %}
<!-- tab 第一个Tab -->
**tab名字为第一个Tab**
<!-- endtab -->

<!-- tab @fab fa-apple-pay -->
**只有图标 没有Tab名字**
<!-- endtab -->

<!-- tab 炸弹@fas fa-bomb -->
**名字+icon**
<!-- endtab -->
{% endtabs %}

\n

tab名字为第一个Tab

只有图标 没有Tab名字

名字+icon

\n

folding

\n
1
2
3
4
5
6
{% folding 参数(可选), 标题 %}

内容

{% endfolding %}

\n

参数位置可以填写颜色和状态,多个参数用空格隔开。

\n
    \n
  • 颜色
  • \n
\n

blue, cyan, green, yellow, red

\n
    \n
  • 状态
  • \n
\n

状态填写 open 代表默认打开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{% folding 查看图片测试 %}

这是内容

{% endfolding %}

{% folding cyan open, 查看默认打开的折叠框 %}

这是一个默认打开的折叠框。

{% endfolding %}

{% folding green, 查看代码测试 %}

{% endfolding %}

{% folding yellow, 查看列表测试 %}

- haha
- hehe

{% endfolding %}

{% folding red, 查看嵌套测试 %}

{% folding blue, 查看嵌套测试2 %}

{% folding 查看嵌套测试3 %}

hahaha <span><img src='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/emoji/tieba/%E6%BB%91%E7%A8%BD.png' style='height:24px'></span>

{% endfolding %}

{% endfolding %}

{% endfolding %}

查看图片测试 \n
\n

这是内容

\n
\n
\n
查看默认打开的折叠框 \n
\n

这是一个默认打开的折叠框。

\n
\n
\n
查看代码测试 \n
\n
          </div>\n        </details>\n
\n
查看列表测试 \n
\n
  • haha
  • hehe
\n
\n
\n
查看嵌套测试 \n
\n
查看嵌套测试2
查看嵌套测试3

hahaha

\n
\n
","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

标签外挂

\n

note

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note simple %}
默认 提示块标籤
{% endnote %}

{% note default simple %}
default 提示块标籤
{% endnote %}

{% note primary simple %}
primary 提示块标籤
{% endnote %}

{% note success simple %}
success 提示块标籤
{% endnote %}

{% note info simple %}
info 提示块标籤
{% endnote %}

{% note warning simple %}
warning 提示块标籤
{% endnote %}

{% note danger simple %}
danger 提示块标籤
{% endnote %}

\n

预览

\n

默认 提示块标籤

\n
\n

default 提示块标籤

\n
\n

primary 提示块标籤

\n
\n

success 提示块标籤

\n
\n

info 提示块标籤

\n
\n

warning 提示块标籤

\n
\n

danger 提示块标籤

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note modern %}
默认 提示块标籤
{% endnote %}

{% note default modern %}
default 提示块标籤
{% endnote %}

{% note primary modern %}
primary 提示块标籤
{% endnote %}

{% note success modern %}
success 提示块标籤
{% endnote %}

{% note info modern %}
info 提示块标籤
{% endnote %}

{% note warning modern %}
warning 提示块标籤
{% endnote %}

{% note danger modern %}
danger 提示块标籤
{% endnote %}

\n

预览

\n

默认 提示块标籤

\n
\n

default 提示块标籤

\n
\n

primary 提示块标籤

\n
\n

success 提示块标籤

\n
\n

info 提示块标籤

\n
\n

warning 提示块标籤

\n
\n

danger 提示块标籤

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note flat %}
默认 提示块标签
{% endnote %}

{% note default flat %}
default 提示块标签
{% endnote %}

{% note primary flat %}
primary 提示块标签
{% endnote %}

{% note success flat %}
success 提示块标签
{% endnote %}

{% note info flat %}
info 提示块标签
{% endnote %}

{% note warning flat %}
warning 提示块标签
{% endnote %}

{% note danger flat %}
danger 提示块标签
{% endnote %}

\n

默认 提示块标签

\n
\n

default 提示块标签

\n
\n

primary 提示块标签

\n
\n

success 提示块标签

\n
\n

info 提示块标签

\n
\n

warning 提示块标签

\n
\n

danger 提示块标签

\n
\n

Tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{% tabs Unique name, [index] %}
<!-- tab [Tab caption] [@icon] -->
Any content (support inline tags too).
<!-- endtab -->
{% endtabs %}

Unique name : Unique name of tabs block tag without comma.
Will be used in #id's as prefix for each tab with their index numbers.
If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.
Only for current url of post/page must be unique!
[index] : Index number of active tab.
If not specified, first tab (1) will be selected.
If index is -1, no tab will be selected. It's will be something like spoiler.
Optional parameter.
[Tab caption] : Caption of current tab.
If not caption specified, unique name with tab index suffix will be used as caption of tab.
If not caption specified, but specified icon, caption will empty.
Optional parameter.
[@icon] : FontAwesome icon name (full-name, look like 'fas fa-font')
Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'.
Optional parameter.

\n

方案一:默认选择第一个tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test1 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案二:默认选择第3个tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test2, 3 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案三:没有预设值

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test3, -1 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案四:自定义每个tab的name和图标icon

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test4 %}
<!-- tab 第一个Tab -->
**tab名字为第一个Tab**
<!-- endtab -->

<!-- tab @fab fa-apple-pay -->
**只有图标 没有Tab名字**
<!-- endtab -->

<!-- tab 炸弹@fas fa-bomb -->
**名字+icon**
<!-- endtab -->
{% endtabs %}

\n

tab名字为第一个Tab

只有图标 没有Tab名字

名字+icon

\n

folding

\n
1
2
3
4
5
6
{% folding 参数(可选), 标题 %}

内容

{% endfolding %}

\n

参数位置可以填写颜色和状态,多个参数用空格隔开。

\n
    \n
  • 颜色
  • \n
\n

blue, cyan, green, yellow, red

\n
    \n
  • 状态
  • \n
\n

状态填写 open 代表默认打开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{% folding 查看图片测试 %}

这是内容

{% endfolding %}

{% folding cyan open, 查看默认打开的折叠框 %}

这是一个默认打开的折叠框。

{% endfolding %}

{% folding green, 查看代码测试 %}

{% endfolding %}

{% folding yellow, 查看列表测试 %}

- haha
- hehe

{% endfolding %}

{% folding red, 查看嵌套测试 %}

{% folding blue, 查看嵌套测试2 %}

{% folding 查看嵌套测试3 %}

hahaha <span><img src='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/emoji/tieba/%E6%BB%91%E7%A8%BD.png' style='height:24px'></span>

{% endfolding %}

{% endfolding %}

{% endfolding %}

查看图片测试 \n
\n

这是内容

\n
\n
\n
查看默认打开的折叠框 \n
\n

这是一个默认打开的折叠框。

\n
\n
\n
查看代码测试 \n
\n
          </div>\n        </details>\n
\n
查看列表测试 \n
\n
  • haha
  • hehe
\n
\n
\n
查看嵌套测试 \n
\n
查看嵌套测试2
查看嵌套测试3

hahaha

\n
\n
"},{"title":"1-maven基本概念","abbrlink":"52e2375","date":"2021-04-17T02:51:47.000Z","description":"maven的基本概念、特性介绍及如何使用","_content":"\n\n# Maven基本概念\n\n## Maven学习内容\n\n\"Maven\"\n\n\n\n## Maven简介\n\nMaven主要用于项目构建、依赖管理、项目信息管理\n\n1. 小型项目\n2. 大型企业项目\n3. 传统瀑布式开发\n4. 流行的敏捷开发\n\n\n\n## 项目构建\n\n​\t\t总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编\n\n译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。\n\n\n\n## 项目构建工具\n\n### Ant\n\n最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。\n\n### Maven\n\n- 项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。 \n- 填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。\n- Maven 专注的是依赖管理, 使用 Java 编写。\n\n### Gradle\n\n- 结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。\n- 它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。\n\n\n\n## Maven四大特性\n\n### 依赖管理系统(jar项目的多模块)\n\n- Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。 \n- 在 Java 世界中, 可以用 groupIdartifactIdversion 组成的Coordination( 坐标)唯一标识一个依赖。 \n- 任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。\n\n一个典型的依赖引用如下所示:\n\n```xml\n\n\tjavax.servlet //包名/项目名\n\tjavax.servlet-api //某一模块名\n\t3.1.0\t//所需要jar的版本\n\n```\n\n### 多模块构建\n\n 项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。\n\n### 一致的项目结构(不同的IDE项目结构是一致)\n\n Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。\n\n\n\n\n\n### 一致的构建模型和插件机制\n\n通过 pom 配置 tomcat、 jetty 插件:\n\n```html\n\n\t\n\torg.mortbay.jetty\n\tmaven-jetty-plugin\n\t6.1.25\n\t\n\t10\n\t/test\n\n\n\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/Maven/1-maven基本概念.md","raw":"---\ntitle: 1-maven基本概念\ntags:\n - Maven\ncategories:\n - - java\n - Maven\nabbrlink: '52e2375'\ndate: 2021-04-17 10:51:47\ndescription: maven的基本概念、特性介绍及如何使用\n---\n\n\n# Maven基本概念\n\n## Maven学习内容\n\n\"Maven\"\n\n\n\n## Maven简介\n\nMaven主要用于项目构建、依赖管理、项目信息管理\n\n1. 小型项目\n2. 大型企业项目\n3. 传统瀑布式开发\n4. 流行的敏捷开发\n\n\n\n## 项目构建\n\n​\t\t总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编\n\n译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。\n\n\n\n## 项目构建工具\n\n### Ant\n\n最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。\n\n### Maven\n\n- 项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。 \n- 填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。\n- Maven 专注的是依赖管理, 使用 Java 编写。\n\n### Gradle\n\n- 结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。\n- 它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。\n\n\n\n## Maven四大特性\n\n### 依赖管理系统(jar项目的多模块)\n\n- Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。 \n- 在 Java 世界中, 可以用 groupIdartifactIdversion 组成的Coordination( 坐标)唯一标识一个依赖。 \n- 任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。\n\n一个典型的依赖引用如下所示:\n\n```xml\n\n\tjavax.servlet //包名/项目名\n\tjavax.servlet-api //某一模块名\n\t3.1.0\t//所需要jar的版本\n\n```\n\n### 多模块构建\n\n 项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。\n\n### 一致的项目结构(不同的IDE项目结构是一致)\n\n Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。\n\n\n\n\n\n### 一致的构建模型和插件机制\n\n通过 pom 配置 tomcat、 jetty 插件:\n\n```html\n\n\t\n\torg.mortbay.jetty\n\tmaven-jetty-plugin\n\t6.1.25\n\t\n\t10\n\t/test\n\n\n\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","slug":"Maven/1-maven基本概念","published":1,"updated":"2021-09-10T02:59:29.812Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oj0013akve45f3gq5i","content":"

Maven基本概念

Maven学习内容

\"Maven\"

\n

Maven简介

Maven主要用于项目构建、依赖管理、项目信息管理

\n
    \n
  1. 小型项目
  2. \n
  3. 大型企业项目
  4. \n
  5. 传统瀑布式开发
  6. \n
  7. 流行的敏捷开发
  8. \n
\n

项目构建

​ 总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编

\n

译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。

\n

项目构建工具

Ant

最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。

\n

Maven

    \n
  • 项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。
  • \n
  • 填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。
  • \n
  • Maven 专注的是依赖管理, 使用 Java 编写。
  • \n
\n

Gradle

    \n
  • 结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。
  • \n
  • 它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。
  • \n
\n

Maven四大特性

依赖管理系统(jar项目的多模块)

    \n
  • Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。
  • \n
  • 在 Java 世界中, 可以用 groupIdartifactIdversion 组成的Coordination( 坐标)唯一标识一个依赖。
  • \n
  • 任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。
  • \n
\n

一个典型的依赖引用如下所示:

\n
1
2
3
4
5
<dependency>
\t<groupId>javax.servlet</groupId> //包名/项目名
\t<artifactId>javax.servlet-api</artifactId> //某一模块名
\t<version>3.1.0</version>\t//所需要jar的版本
</dependency>
\n

多模块构建

项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。

\n

一致的项目结构(不同的IDE项目结构是一致)

Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。

\n

一致的构建模型和插件机制

通过 pom 配置 tomcat、 jetty 插件:

\n
1
2
3
4
5
6
7
8
9
10
11
<plugin>
\t<!--这里是所有添加插件配置的地方-->
\t<groupId>org.mortbay.jetty</groupId>
\t<artifactId>maven-jetty-plugin</artifactId>
\t<version>6.1.25</version>
\t<configuration>
\t<scanIntervalSeconds>10</scanIntervalSeconds>
\t<contextPath>/test</contextPath>
</configuration>
</plugin>

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"

Maven基本概念

Maven学习内容

\"Maven\"

\n

Maven简介

Maven主要用于项目构建、依赖管理、项目信息管理

\n
    \n
  1. 小型项目
  2. \n
  3. 大型企业项目
  4. \n
  5. 传统瀑布式开发
  6. \n
  7. 流行的敏捷开发
  8. \n
\n

项目构建

​ 总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编

\n

译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。

\n

项目构建工具

Ant

最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。

\n

Maven

    \n
  • 项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。
  • \n
  • 填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。
  • \n
  • Maven 专注的是依赖管理, 使用 Java 编写。
  • \n
\n

Gradle

    \n
  • 结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。
  • \n
  • 它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。
  • \n
\n

Maven四大特性

依赖管理系统(jar项目的多模块)

    \n
  • Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。
  • \n
  • 在 Java 世界中, 可以用 groupIdartifactIdversion 组成的Coordination( 坐标)唯一标识一个依赖。
  • \n
  • 任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。
  • \n
\n

一个典型的依赖引用如下所示:

\n
1
2
3
4
5
<dependency>
\t<groupId>javax.servlet</groupId> //包名/项目名
\t<artifactId>javax.servlet-api</artifactId> //某一模块名
\t<version>3.1.0</version>\t//所需要jar的版本
</dependency>
\n

多模块构建

项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。

\n

一致的项目结构(不同的IDE项目结构是一致)

Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。

\n

一致的构建模型和插件机制

通过 pom 配置 tomcat、 jetty 插件:

\n
1
2
3
4
5
6
7
8
9
10
11
<plugin>
\t<!--这里是所有添加插件配置的地方-->
\t<groupId>org.mortbay.jetty</groupId>
\t<artifactId>maven-jetty-plugin</artifactId>
\t<version>6.1.25</version>
\t<configuration>
\t<scanIntervalSeconds>10</scanIntervalSeconds>
\t<contextPath>/test</contextPath>
</configuration>
</plugin>

\n"},{"title":"3-maven+IDEA创建webapp","abbrlink":"79c0e989","date":"2021-04-17T11:54:41.000Z","description":"如何用idea编辑器、maven工具构建一个webapp","_content":"\n\n\n\n\n### 创建project\n\n![image-20210502143531044](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-35-33_image-20210502143531044.png)\n\n\n\n![image-20210502143822062](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-38-23_image-20210502143822062.png)\n\n\n\n![image-20210502144127624](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-41-29_image-20210502144127624.png)\n\n接着等待下载一些文件\n\n进入工程下文件 `pom.xml`,做以下配置:\n\n修改Java编译器版本为`1.8`(根据自己情况)\n\n```xml\n\n UTF-8\n 1.8\n 1.8\n\n```\n\n\n\n删除下面两个标签及标签中的内容\n\n```xml\n\n ...\n\n```\n\n如果使用tomcat服务器,在刚才删除的地方添加下面内容\n\n```xml\n\n \n org.apache.tomcat.maven\n tomcat7-maven-plugin\n 2.2 \n \n 8088 \n /test \n UTF-8\n tomcat7\n \n \n\n```\n\n\n\n然后:\n\n![image-20210502145452277](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-54-53_image-20210502145452277.png)\n\n\n\n等待下载完成后\n\n![image-20210502145735190](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-57-36_image-20210502145735190.png)\n\n![image-20210502145859423](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-59-00_image-20210502145859423.png)\n\n![image-20210502150218246](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-02-19_image-20210502150218246.png)\n\n\n\n\n\n配置完成,点击运行\n\n![image-20210502150451149](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-04-52_image-20210502150451149.png)\n\n![image-20210502150620073](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-06-21_image-20210502150620073.png)\n\n浏览器访问结果:\n\n![image-20210502150700084](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-07-01_image-20210502150700084.png)\n\n\n\n\n\n\n\n\n> 下载的插件在repository的org目录下\n>\n> ```\n> D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat\n> ```","source":"_posts/Maven/3-maven+IDEA创建webapp.md","raw":"---\ntitle: 3-maven+IDEA创建webapp\ntags:\n - Maven\ncategories:\n - - java\n - Maven\nabbrlink: 79c0e989\ndate: 2021-04-17 19:54:41\ndescription: 如何用idea编辑器、maven工具构建一个webapp\n---\n\n\n\n\n\n### 创建project\n\n![image-20210502143531044](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-35-33_image-20210502143531044.png)\n\n\n\n![image-20210502143822062](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-38-23_image-20210502143822062.png)\n\n\n\n![image-20210502144127624](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-41-29_image-20210502144127624.png)\n\n接着等待下载一些文件\n\n进入工程下文件 `pom.xml`,做以下配置:\n\n修改Java编译器版本为`1.8`(根据自己情况)\n\n```xml\n\n UTF-8\n 1.8\n 1.8\n\n```\n\n\n\n删除下面两个标签及标签中的内容\n\n```xml\n\n ...\n\n```\n\n如果使用tomcat服务器,在刚才删除的地方添加下面内容\n\n```xml\n\n \n org.apache.tomcat.maven\n tomcat7-maven-plugin\n 2.2 \n \n 8088 \n /test \n UTF-8\n tomcat7\n \n \n\n```\n\n\n\n然后:\n\n![image-20210502145452277](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-54-53_image-20210502145452277.png)\n\n\n\n等待下载完成后\n\n![image-20210502145735190](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-57-36_image-20210502145735190.png)\n\n![image-20210502145859423](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-59-00_image-20210502145859423.png)\n\n![image-20210502150218246](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-02-19_image-20210502150218246.png)\n\n\n\n\n\n配置完成,点击运行\n\n![image-20210502150451149](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-04-52_image-20210502150451149.png)\n\n![image-20210502150620073](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-06-21_image-20210502150620073.png)\n\n浏览器访问结果:\n\n![image-20210502150700084](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-07-01_image-20210502150700084.png)\n\n\n\n\n\n\n\n\n> 下载的插件在repository的org目录下\n>\n> ```\n> D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat\n> ```","slug":"Maven/3-maven+IDEA创建webapp","published":1,"updated":"2021-09-10T03:00:13.294Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ok0018akve39f5g6yx","content":"

创建project

\"image-20210502143531044\"

\n

\"image-20210502143822062\"

\n

\"image-20210502144127624\"

\n

接着等待下载一些文件

\n

进入工程下文件 pom.xml,做以下配置:

\n

修改Java编译器版本为1.8(根据自己情况)

\n
1
2
3
4
5
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
\n

删除下面两个标签及标签中的内容

\n
1
2
3
<pluginManagement>
...
</pluginManagement>
\n

如果使用tomcat服务器,在刚才删除的地方添加下面内容

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version> <!--tomcat版本-->
<configuration>
<port>8088</port> <!--端口-->
<path>/test</path> <!--项目站点名-->
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
\n

然后:

\n

\"image-20210502145452277\"

\n

等待下载完成后

\n

\"image-20210502145735190\"

\n

\"image-20210502145859423\"

\n

\"image-20210502150218246\"

\n

配置完成,点击运行

\n

\"image-20210502150451149\"

\n

\"image-20210502150620073\"

\n

浏览器访问结果:

\n

\"image-20210502150700084\"

\n
\n

下载的插件在repository的org目录下

\n
1
D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

创建project

\"image-20210502143531044\"

\n

\"image-20210502143822062\"

\n

\"image-20210502144127624\"

\n

接着等待下载一些文件

\n

进入工程下文件 pom.xml,做以下配置:

\n

修改Java编译器版本为1.8(根据自己情况)

\n
1
2
3
4
5
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
\n

删除下面两个标签及标签中的内容

\n
1
2
3
<pluginManagement>
...
</pluginManagement>
\n

如果使用tomcat服务器,在刚才删除的地方添加下面内容

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version> <!--tomcat版本-->
<configuration>
<port>8088</port> <!--端口-->
<path>/test</path> <!--项目站点名-->
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
\n

然后:

\n

\"image-20210502145452277\"

\n

等待下载完成后

\n

\"image-20210502145735190\"

\n

\"image-20210502145859423\"

\n

\"image-20210502150218246\"

\n

配置完成,点击运行

\n

\"image-20210502150451149\"

\n

\"image-20210502150620073\"

\n

浏览器访问结果:

\n

\"image-20210502150700084\"

\n
\n

下载的插件在repository的org目录下

\n
1
D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat
\n"},{"title":"MySql(2)-MySQL基础","abbrlink":"39aef30f","date":"2021-06-05T04:18:30.000Z","description":"MySQL介绍与SQL语言使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n\n\n# MySQL基础\n\n\n\n## mysql语法\n\n注释:\n\n- 单行注释:`--这是注释`\n\n- 多行注释:\n\n ```mysql\n /*\n 这是注释\n 这是注释\n 这是注释\n 这是注释\n */\n ```\n\n\nmysql语句不区分大小写,建议小写\n\n## 数据库基本操作\n\n操作数据库->操作数据库的表->操作数据库的表中的数据\n\n\n\n连接数据库\n\n```shell\nmysql -uroot -p密码 #-p和密码之间没有空格\n```\n\n\n\n查看全部数据库\n\n```mysql\nshow databases;\n```\n\n使用某个数据库(假设有个数据库为:school)\n\n```mysql\nuse school;\n```\n\n> 如果名称是sql中的一些关键字,要加反引号\n>\n> ```mysql\n> use `user`;\n> ```\n\n查看该数据库所有的表\n\n```mysql\nshow tables;\n```\n\n查看某个表信息(假设在该数据库中有个表为:student):\n\n```mysql\ndescribe student;\n或\ndesc student;\n```\n\n创建数据库school; (中括号 `[]` 表示可选)\n\n```mysql\ncreate database [if not exist] school;\n```\n\n删除数据库:\n\n```mysql\ndrop database [if exist] school;\n```\n\n\n\n## 数据库的(列)数据类型\n\n### 数值\n\n1. tinyint: 1个字节(-127~128)\n2. smallint: 2个字节\n3. mediumint: 3个字节\n4. **int:4个字节**\n5. bigint:8个字节\n6. float:4个字节\n7. double:8个字节\n8. decimal:字符串形式的浮点数(金融计算常用)\n\n### 字符串\n\n1. char:1个字节(0~255)\n2. **varchar:可变字符串 0~65535**\n3. tinytext: 微型文本 2^8-1\n4. **text:文本串 2^16-1**\n\n\n\n\n\n### 时间日期\n\n1. date\t\tYYYY-MM-DD, 日期格式\n2. time HH:mm:ss 时间格式\n3. **datetime** YYYY-MM-DD HH:mm:ss (最常用)\n4. **timestamp** 1970.1.1到现在的毫秒数(较常用)\n\n5. year:年份\n\n\n\n### NULL\n\n没有值,未知\n\n注意,不要使用NULL进行运算,结果为NULL\n\n\n\n## 字段属性(重要)\n\n每一列(称为字段)都有多个属性,用于描述该列的特征\n\n- unsigned\n\n 无符号整数,表示该列不能为负数\n\n\n\n- zerofill\n - 0填充的\n - 不足的位数用0填充,如:int(3); --输入数字5,自动转为 `005`\n\n\n\n- 自增 auto_increment\n\n 自动在一条记录的基础上(默认) `+1`\n\n 必须是整数类型\n\n\n\n\n\n- 非空 NOT NULL\n - 假设为 not null: 如果不给他赋值,会报错\n\n\n\n例子:创建一个表\n\n```mysql\nCREATE TABLE\nIF\n\tNOT EXISTS `student` (\n\t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号',\n\t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',\n\t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码',\n\t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别',\n\t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄',\n\tPRIMARY KEY ( `id` ) -- 主键\n\t\n\t) ENGINE = INNODB DEFAULT CHARSET = utf8;\n```\n\n\n\n创建表格式( `[]`里面的内容表示可有可无 ):\n\n```mysql\nCREATE TABLE [IF NOT EXISTS] `表名`(\n\t`字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n ...\n `字段名` 列类型 [属性] [索引] [注释]\n)[表类型] [字符集设置] [注释]\n```\n\n\n\n在创建完一个数据库或者表后,可以用以下语句:\n\n```mysql\nSHOW CREATE DATABASE school; -- 查看创建数据库的语句\nSHOW CREATE TABLE student; \t-- 查看创建表的语句\nDESC student; -- 显示表的结构\n```\n\n\n\n\n\n## 数据库引擎\n\n1. INNODB(默认使用)\n2. MYISAM\n\n\n\n| | MYISAM | INNODB |\n| ------------ | ------ | ------------------- |\n| 事务支持 | 不支持 | 支持 |\n| 数据行锁定 | 不支持 | 支持 |\n| 外键约束 | 不支持 | 支持 |\n| 全文索引 | 支持 | 不支持 |\n| 表的空间大小 | 较小 | 较大,为MYISAM的2倍 |\n\n事务\n\n- 是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以\n\n\n\n占用空间:\n\n- InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;\n\n- 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。\n\n\n\n常用操作:\n\n- MYISAM: 节约空间,速度快\n- INNODB:安全性高,事务处理,多表多用户操作\n\n\n\n物理空间存在位置:\n\n所有数据文件都在data目录下,本质还是文件存储\n\n- MYISAM对应文件:\n - *.frm 表结构定义\n - *.MYD 数据文件\n - *.MYI 索引文件\n- INNODB:只有 *.frm 文件,以及上级目录下ibdata1文件\n\n\n\n## 设置字符集编码\n\n```MySQL\nCHARSET=utf8\n```\n\nMySQL默认字符集 Latin1, 不支持中文\n\n可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上\n\n\n\n\n\n## 修改、删除表\n\n### 修改表的结构:ALTER TABLE\n\n修改表名\n\n```MYSQL\nALTER TABLE teacher RENAME AS teacher1;\n```\n\n增加表的字段\n\n```mysql\nALTER TABLE teacher1 ADD age INT(11)\n```\n\n修改表的字段(字段名,字段类型)\n\n```mysql\nALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改l\nALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名\n\n```\n\n删除表的字段\n\n```mysql\nALTER TABLE teacher1 DROP age1; \n```\n\n\n\n### 删除表 DROP TABLE\n\n```mysql\ndrop table [if exists] teacher; -- 一般加上判断,避免出错\n```\n\n\n\n\n\n","source":"_posts/MySQL/2-MySQL基础.md","raw":"---\ntitle: MySql(2)-MySQL基础\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 39aef30f\ndate: 2021-06-05 12:18:30\ndescription: MySQL介绍与SQL语言使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n\n\n# MySQL基础\n\n\n\n## mysql语法\n\n注释:\n\n- 单行注释:`--这是注释`\n\n- 多行注释:\n\n ```mysql\n /*\n 这是注释\n 这是注释\n 这是注释\n 这是注释\n */\n ```\n\n\nmysql语句不区分大小写,建议小写\n\n## 数据库基本操作\n\n操作数据库->操作数据库的表->操作数据库的表中的数据\n\n\n\n连接数据库\n\n```shell\nmysql -uroot -p密码 #-p和密码之间没有空格\n```\n\n\n\n查看全部数据库\n\n```mysql\nshow databases;\n```\n\n使用某个数据库(假设有个数据库为:school)\n\n```mysql\nuse school;\n```\n\n> 如果名称是sql中的一些关键字,要加反引号\n>\n> ```mysql\n> use `user`;\n> ```\n\n查看该数据库所有的表\n\n```mysql\nshow tables;\n```\n\n查看某个表信息(假设在该数据库中有个表为:student):\n\n```mysql\ndescribe student;\n或\ndesc student;\n```\n\n创建数据库school; (中括号 `[]` 表示可选)\n\n```mysql\ncreate database [if not exist] school;\n```\n\n删除数据库:\n\n```mysql\ndrop database [if exist] school;\n```\n\n\n\n## 数据库的(列)数据类型\n\n### 数值\n\n1. tinyint: 1个字节(-127~128)\n2. smallint: 2个字节\n3. mediumint: 3个字节\n4. **int:4个字节**\n5. bigint:8个字节\n6. float:4个字节\n7. double:8个字节\n8. decimal:字符串形式的浮点数(金融计算常用)\n\n### 字符串\n\n1. char:1个字节(0~255)\n2. **varchar:可变字符串 0~65535**\n3. tinytext: 微型文本 2^8-1\n4. **text:文本串 2^16-1**\n\n\n\n\n\n### 时间日期\n\n1. date\t\tYYYY-MM-DD, 日期格式\n2. time HH:mm:ss 时间格式\n3. **datetime** YYYY-MM-DD HH:mm:ss (最常用)\n4. **timestamp** 1970.1.1到现在的毫秒数(较常用)\n\n5. year:年份\n\n\n\n### NULL\n\n没有值,未知\n\n注意,不要使用NULL进行运算,结果为NULL\n\n\n\n## 字段属性(重要)\n\n每一列(称为字段)都有多个属性,用于描述该列的特征\n\n- unsigned\n\n 无符号整数,表示该列不能为负数\n\n\n\n- zerofill\n - 0填充的\n - 不足的位数用0填充,如:int(3); --输入数字5,自动转为 `005`\n\n\n\n- 自增 auto_increment\n\n 自动在一条记录的基础上(默认) `+1`\n\n 必须是整数类型\n\n\n\n\n\n- 非空 NOT NULL\n - 假设为 not null: 如果不给他赋值,会报错\n\n\n\n例子:创建一个表\n\n```mysql\nCREATE TABLE\nIF\n\tNOT EXISTS `student` (\n\t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号',\n\t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',\n\t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码',\n\t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别',\n\t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄',\n\tPRIMARY KEY ( `id` ) -- 主键\n\t\n\t) ENGINE = INNODB DEFAULT CHARSET = utf8;\n```\n\n\n\n创建表格式( `[]`里面的内容表示可有可无 ):\n\n```mysql\nCREATE TABLE [IF NOT EXISTS] `表名`(\n\t`字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n ...\n `字段名` 列类型 [属性] [索引] [注释]\n)[表类型] [字符集设置] [注释]\n```\n\n\n\n在创建完一个数据库或者表后,可以用以下语句:\n\n```mysql\nSHOW CREATE DATABASE school; -- 查看创建数据库的语句\nSHOW CREATE TABLE student; \t-- 查看创建表的语句\nDESC student; -- 显示表的结构\n```\n\n\n\n\n\n## 数据库引擎\n\n1. INNODB(默认使用)\n2. MYISAM\n\n\n\n| | MYISAM | INNODB |\n| ------------ | ------ | ------------------- |\n| 事务支持 | 不支持 | 支持 |\n| 数据行锁定 | 不支持 | 支持 |\n| 外键约束 | 不支持 | 支持 |\n| 全文索引 | 支持 | 不支持 |\n| 表的空间大小 | 较小 | 较大,为MYISAM的2倍 |\n\n事务\n\n- 是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以\n\n\n\n占用空间:\n\n- InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;\n\n- 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。\n\n\n\n常用操作:\n\n- MYISAM: 节约空间,速度快\n- INNODB:安全性高,事务处理,多表多用户操作\n\n\n\n物理空间存在位置:\n\n所有数据文件都在data目录下,本质还是文件存储\n\n- MYISAM对应文件:\n - *.frm 表结构定义\n - *.MYD 数据文件\n - *.MYI 索引文件\n- INNODB:只有 *.frm 文件,以及上级目录下ibdata1文件\n\n\n\n## 设置字符集编码\n\n```MySQL\nCHARSET=utf8\n```\n\nMySQL默认字符集 Latin1, 不支持中文\n\n可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上\n\n\n\n\n\n## 修改、删除表\n\n### 修改表的结构:ALTER TABLE\n\n修改表名\n\n```MYSQL\nALTER TABLE teacher RENAME AS teacher1;\n```\n\n增加表的字段\n\n```mysql\nALTER TABLE teacher1 ADD age INT(11)\n```\n\n修改表的字段(字段名,字段类型)\n\n```mysql\nALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改l\nALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名\n\n```\n\n删除表的字段\n\n```mysql\nALTER TABLE teacher1 DROP age1; \n```\n\n\n\n### 删除表 DROP TABLE\n\n```mysql\ndrop table [if exists] teacher; -- 一般加上判断,避免出错\n```\n\n\n\n\n\n","slug":"MySQL/2-MySQL基础","published":1,"updated":"2021-09-08T13:06:19.369Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ol001bakve1lqxghp8","content":"

MySQL基础

mysql语法

注释:

\n
    \n
  • 单行注释:--这是注释

    \n
  • \n
  • 多行注释:

    \n
    1
    2
    3
    4
    5
    6
    /*
    这是注释
    这是注释
    这是注释
    这是注释
    */
    \n
  • \n
\n

mysql语句不区分大小写,建议小写

\n

数据库基本操作

操作数据库->操作数据库的表->操作数据库的表中的数据

\n

连接数据库

\n
1
mysql -uroot -p密码          #-p和密码之间没有空格
\n

查看全部数据库

\n
1
show databases;
\n

使用某个数据库(假设有个数据库为:school)

\n
1
use school;
\n
\n

如果名称是sql中的一些关键字,要加反引号

\n
1
use `user`;
\n
\n

查看该数据库所有的表

\n
1
show tables;
\n

查看某个表信息(假设在该数据库中有个表为:student):

\n
1
2
3
describe student;

desc student;
\n

创建数据库school; (中括号 [] 表示可选)

\n
1
create database [if not exist] school;
\n

删除数据库:

\n
1
drop database [if exist] school;
\n

数据库的(列)数据类型

数值

    \n
  1. tinyint: 1个字节(-127~128)
  2. \n
  3. smallint: 2个字节
  4. \n
  5. mediumint: 3个字节
  6. \n
  7. int:4个字节
  8. \n
  9. bigint:8个字节
  10. \n
  11. float:4个字节
  12. \n
  13. double:8个字节
  14. \n
  15. decimal:字符串形式的浮点数(金融计算常用)
  16. \n
\n

字符串

    \n
  1. char:1个字节(0~255)
  2. \n
  3. varchar:可变字符串 0~65535
  4. \n
  5. tinytext: 微型文本 2^8-1
  6. \n
  7. text:文本串 2^16-1
  8. \n
\n

时间日期

    \n
  1. date YYYY-MM-DD, 日期格式
  2. \n
  3. time HH:mm:ss 时间格式
  4. \n
  5. datetime YYYY-MM-DD HH:mm:ss (最常用)
  6. \n
  7. timestamp 1970.1.1到现在的毫秒数(较常用)

    \n
  8. \n
  9. year:年份

    \n
  10. \n
\n

NULL

没有值,未知

\n

注意,不要使用NULL进行运算,结果为NULL

\n

字段属性(重要)

每一列(称为字段)都有多个属性,用于描述该列的特征

\n
    \n
  • unsigned

    \n

    无符号整数,表示该列不能为负数

    \n
  • \n
\n
    \n
  • zerofill
      \n
    • 0填充的
    • \n
    • 不足的位数用0填充,如:int(3); —输入数字5,自动转为 005
    • \n
    \n
  • \n
\n
    \n
  • 自增 auto_increment

    \n

    自动在一条记录的基础上(默认) +1

    \n

    必须是整数类型

    \n
  • \n
\n
    \n
  • 非空 NOT NULL
      \n
    • 假设为 not null: 如果不给他赋值,会报错
    • \n
    \n
  • \n
\n

例子:创建一个表

\n
1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE
IF
\tNOT EXISTS `student` (
\t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号',
\t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',
\t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码',
\t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别',
\t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄',
\tPRIMARY KEY ( `id` ) -- 主键
\t
\t) ENGINE = INNODB DEFAULT CHARSET = utf8;
\n

创建表格式( []里面的内容表示可有可无 ):

\n
1
2
3
4
5
6
7
8
CREATE TABLE [IF NOT EXISTS] `表名`(
\t`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
...
`字段名` 列类型 [属性] [索引] [注释]
)[表类型] [字符集设置] [注释]
\n

在创建完一个数据库或者表后,可以用以下语句:

\n
1
2
3
SHOW CREATE DATABASE school; -- 查看创建数据库的语句
SHOW CREATE TABLE student; \t-- 查看创建表的语句
DESC student; -- 显示表的结构
\n

数据库引擎

    \n
  1. INNODB(默认使用)
  2. \n
  3. MYISAM
  4. \n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
MYISAMINNODB
事务支持不支持支持
数据行锁定不支持支持
外键约束不支持支持
全文索引支持不支持
表的空间大小较小较大,为MYISAM的2倍
\n
\n

事务

\n
    \n
  • 是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以
  • \n
\n

占用空间:

\n
    \n
  • InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;

    \n
  • \n
  • 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。

    \n
  • \n
\n

常用操作:

\n
    \n
  • MYISAM: 节约空间,速度快
  • \n
  • INNODB:安全性高,事务处理,多表多用户操作
  • \n
\n

物理空间存在位置:

\n

所有数据文件都在data目录下,本质还是文件存储

\n
    \n
  • MYISAM对应文件:
      \n
    • *.frm 表结构定义
    • \n
    • *.MYD 数据文件
    • \n
    • *.MYI 索引文件
    • \n
    \n
  • \n
  • INNODB:只有 *.frm 文件,以及上级目录下ibdata1文件
  • \n
\n

设置字符集编码

1
CHARSET=utf8
\n

MySQL默认字符集 Latin1, 不支持中文

\n

可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上

\n

修改、删除表

修改表的结构:ALTER TABLE

修改表名

\n
1
ALTER TABLE teacher RENAME AS teacher1;
\n

增加表的字段

\n
1
ALTER TABLE teacher1 ADD age INT(11)
\n

修改表的字段(字段名,字段类型)

\n
1
2
3
ALTER TABLE teacher1 MODIFY age VARCHAR(11)  -- 修改l
ALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名

\n

删除表的字段

\n
1
ALTER TABLE teacher1 DROP age1; 
\n

删除表 DROP TABLE

1
drop table [if exists] teacher;   -- 一般加上判断,避免出错
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

MySQL基础

mysql语法

注释:

\n
    \n
  • 单行注释:--这是注释

    \n
  • \n
  • 多行注释:

    \n
    1
    2
    3
    4
    5
    6
    /*
    这是注释
    这是注释
    这是注释
    这是注释
    */
    \n
  • \n
\n

mysql语句不区分大小写,建议小写

\n

数据库基本操作

操作数据库->操作数据库的表->操作数据库的表中的数据

\n

连接数据库

\n
1
mysql -uroot -p密码          #-p和密码之间没有空格
\n

查看全部数据库

\n
1
show databases;
\n

使用某个数据库(假设有个数据库为:school)

\n
1
use school;
\n
\n

如果名称是sql中的一些关键字,要加反引号

\n
1
use `user`;
\n
\n

查看该数据库所有的表

\n
1
show tables;
\n

查看某个表信息(假设在该数据库中有个表为:student):

\n
1
2
3
describe student;

desc student;
\n

创建数据库school; (中括号 [] 表示可选)

\n
1
create database [if not exist] school;
\n

删除数据库:

\n
1
drop database [if exist] school;
\n

数据库的(列)数据类型

数值

    \n
  1. tinyint: 1个字节(-127~128)
  2. \n
  3. smallint: 2个字节
  4. \n
  5. mediumint: 3个字节
  6. \n
  7. int:4个字节
  8. \n
  9. bigint:8个字节
  10. \n
  11. float:4个字节
  12. \n
  13. double:8个字节
  14. \n
  15. decimal:字符串形式的浮点数(金融计算常用)
  16. \n
\n

字符串

    \n
  1. char:1个字节(0~255)
  2. \n
  3. varchar:可变字符串 0~65535
  4. \n
  5. tinytext: 微型文本 2^8-1
  6. \n
  7. text:文本串 2^16-1
  8. \n
\n

时间日期

    \n
  1. date YYYY-MM-DD, 日期格式
  2. \n
  3. time HH:mm:ss 时间格式
  4. \n
  5. datetime YYYY-MM-DD HH:mm:ss (最常用)
  6. \n
  7. timestamp 1970.1.1到现在的毫秒数(较常用)

    \n
  8. \n
  9. year:年份

    \n
  10. \n
\n

NULL

没有值,未知

\n

注意,不要使用NULL进行运算,结果为NULL

\n

字段属性(重要)

每一列(称为字段)都有多个属性,用于描述该列的特征

\n
    \n
  • unsigned

    \n

    无符号整数,表示该列不能为负数

    \n
  • \n
\n
    \n
  • zerofill
      \n
    • 0填充的
    • \n
    • 不足的位数用0填充,如:int(3); —输入数字5,自动转为 005
    • \n
    \n
  • \n
\n
    \n
  • 自增 auto_increment

    \n

    自动在一条记录的基础上(默认) +1

    \n

    必须是整数类型

    \n
  • \n
\n
    \n
  • 非空 NOT NULL
      \n
    • 假设为 not null: 如果不给他赋值,会报错
    • \n
    \n
  • \n
\n

例子:创建一个表

\n
1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE
IF
\tNOT EXISTS `student` (
\t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号',
\t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',
\t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码',
\t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别',
\t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄',
\tPRIMARY KEY ( `id` ) -- 主键
\t
\t) ENGINE = INNODB DEFAULT CHARSET = utf8;
\n

创建表格式( []里面的内容表示可有可无 ):

\n
1
2
3
4
5
6
7
8
CREATE TABLE [IF NOT EXISTS] `表名`(
\t`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
...
`字段名` 列类型 [属性] [索引] [注释]
)[表类型] [字符集设置] [注释]
\n

在创建完一个数据库或者表后,可以用以下语句:

\n
1
2
3
SHOW CREATE DATABASE school; -- 查看创建数据库的语句
SHOW CREATE TABLE student; \t-- 查看创建表的语句
DESC student; -- 显示表的结构
\n

数据库引擎

    \n
  1. INNODB(默认使用)
  2. \n
  3. MYISAM
  4. \n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
MYISAMINNODB
事务支持不支持支持
数据行锁定不支持支持
外键约束不支持支持
全文索引支持不支持
表的空间大小较小较大,为MYISAM的2倍
\n
\n

事务

\n
    \n
  • 是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以
  • \n
\n

占用空间:

\n
    \n
  • InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;

    \n
  • \n
  • 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。

    \n
  • \n
\n

常用操作:

\n
    \n
  • MYISAM: 节约空间,速度快
  • \n
  • INNODB:安全性高,事务处理,多表多用户操作
  • \n
\n

物理空间存在位置:

\n

所有数据文件都在data目录下,本质还是文件存储

\n
    \n
  • MYISAM对应文件:
      \n
    • *.frm 表结构定义
    • \n
    • *.MYD 数据文件
    • \n
    • *.MYI 索引文件
    • \n
    \n
  • \n
  • INNODB:只有 *.frm 文件,以及上级目录下ibdata1文件
  • \n
\n

设置字符集编码

1
CHARSET=utf8
\n

MySQL默认字符集 Latin1, 不支持中文

\n

可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上

\n

修改、删除表

修改表的结构:ALTER TABLE

修改表名

\n
1
ALTER TABLE teacher RENAME AS teacher1;
\n

增加表的字段

\n
1
ALTER TABLE teacher1 ADD age INT(11)
\n

修改表的字段(字段名,字段类型)

\n
1
2
3
ALTER TABLE teacher1 MODIFY age VARCHAR(11)  -- 修改l
ALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名

\n

删除表的字段

\n
1
ALTER TABLE teacher1 DROP age1; 
\n

删除表 DROP TABLE

1
drop table [if exists] teacher;   -- 一般加上判断,避免出错
\n"},{"title":"MySql(1)-数据库认识","abbrlink":"b9bb0bd5","date":"2021-06-05T02:25:15.000Z","description":"数据库介绍(数据模型、系统结构与组成)","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n# 数据库介绍与认识\n\n\n\n数据库管理系统:DBMS(database manage system)\n\n## 数据模型\n\n- 概念模型:不依赖于任何计算机系统和DBMS的数据模型\n- 逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计\n\n> 组成要素:\n>\n> - 数据结构\n> - 数据操作\n> - 数据完整性的约束条件\n\n\n\n## 概念模型\n\n概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:**实体-联系方法(E-R方法)**\n\n### 实体联系模型\n\n- 实体(Entity):客观世界事物的集合,如学生、教师……\n- 属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号……\n- 码(Key):唯一标识实体的属性集合,如学号\n- 域(Domain):属性的取值范围,如性别有男、女\n\n\n\n> - 实体型\n>\n> - 实体集\n> - 联系\n\n\n\n### 实体联系类型\n\n- 一对一(1:1)\n- 一对多(1:N)\n- 多对多(M:N)\n\n\n\n## 常用数据模型\n\n### 层次模型\n\n用**树形结构**来表示各类实体以及实体间的联系的数据模型\n\n1. 数据结构\n - 有且只有一个结点,无父结点,该结点为根结点\n - 根结点外的其他结点有且仅有一个父结点\n2. 表现形式(以多对多为例)\n - 冗余结点法\n - 虚拟结点法\n3. 数据操作和完整性约束\n - 数据操作:增删查改\n - 完整性约束\n4. 数据存储形式\n - 邻接法\n - 链接法\n5. 优缺点\n - 优点:结构简单,查询效率高\n - 缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发\n\n\n\n### 网状模型\n\n用**网状结构**来表示各类实体之间的联系的数据模型\n\n1. 数据结构\n - 允许一个以上结点无双亲\n - 一个结点可以有多于一个的双亲\n2. 优缺点:\n - 优点:描述方便,存储效率高\n - 缺点:结构复杂,处理复杂\n\n\n\n### 关系模型\n\n用**关系**来表示实体与实体间的联系\n\n1. 数据结构\n\n - 一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系\n - 元组:表的一行称为元组,一个元组可以表示一个实体或实体间的联系\n - 属性:表中的一列称为关系的一个属性\n - 分量:元组中的一个属性值\n\n\"image-20210323234510823\"\n\n2. 数据操作:增删查改\n3. 完整性约束:实体完整性,参照完整性、用户自定义完整性\n\n\n\n## 系统结构\n\n### 二级映像功能\n\n- 模式/内模式映像\n- 外模式/模式映像\n\n\n\n## 数据库组成\n\n1. 数据库\n2. DBMS\n3. 应用程序\n4. 数据库管理员(DBA)\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/MySQL/1-数据库认识.md","raw":"---\ntitle: MySql(1)-数据库认识\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: b9bb0bd5\ndate: 2021-06-05 10:25:15\ndescription: 数据库介绍(数据模型、系统结构与组成)\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n# 数据库介绍与认识\n\n\n\n数据库管理系统:DBMS(database manage system)\n\n## 数据模型\n\n- 概念模型:不依赖于任何计算机系统和DBMS的数据模型\n- 逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计\n\n> 组成要素:\n>\n> - 数据结构\n> - 数据操作\n> - 数据完整性的约束条件\n\n\n\n## 概念模型\n\n概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:**实体-联系方法(E-R方法)**\n\n### 实体联系模型\n\n- 实体(Entity):客观世界事物的集合,如学生、教师……\n- 属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号……\n- 码(Key):唯一标识实体的属性集合,如学号\n- 域(Domain):属性的取值范围,如性别有男、女\n\n\n\n> - 实体型\n>\n> - 实体集\n> - 联系\n\n\n\n### 实体联系类型\n\n- 一对一(1:1)\n- 一对多(1:N)\n- 多对多(M:N)\n\n\n\n## 常用数据模型\n\n### 层次模型\n\n用**树形结构**来表示各类实体以及实体间的联系的数据模型\n\n1. 数据结构\n - 有且只有一个结点,无父结点,该结点为根结点\n - 根结点外的其他结点有且仅有一个父结点\n2. 表现形式(以多对多为例)\n - 冗余结点法\n - 虚拟结点法\n3. 数据操作和完整性约束\n - 数据操作:增删查改\n - 完整性约束\n4. 数据存储形式\n - 邻接法\n - 链接法\n5. 优缺点\n - 优点:结构简单,查询效率高\n - 缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发\n\n\n\n### 网状模型\n\n用**网状结构**来表示各类实体之间的联系的数据模型\n\n1. 数据结构\n - 允许一个以上结点无双亲\n - 一个结点可以有多于一个的双亲\n2. 优缺点:\n - 优点:描述方便,存储效率高\n - 缺点:结构复杂,处理复杂\n\n\n\n### 关系模型\n\n用**关系**来表示实体与实体间的联系\n\n1. 数据结构\n\n - 一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系\n - 元组:表的一行称为元组,一个元组可以表示一个实体或实体间的联系\n - 属性:表中的一列称为关系的一个属性\n - 分量:元组中的一个属性值\n\n\"image-20210323234510823\"\n\n2. 数据操作:增删查改\n3. 完整性约束:实体完整性,参照完整性、用户自定义完整性\n\n\n\n## 系统结构\n\n### 二级映像功能\n\n- 模式/内模式映像\n- 外模式/模式映像\n\n\n\n## 数据库组成\n\n1. 数据库\n2. DBMS\n3. 应用程序\n4. 数据库管理员(DBA)\n\n\n\n\n\n\n\n\n\n\n\n","slug":"MySQL/1-数据库认识","published":1,"updated":"2021-09-10T03:01:10.897Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oo001gakve5kd5ad6i","content":"

数据库介绍与认识

数据库管理系统:DBMS(database manage system)

\n

数据模型

    \n
  • 概念模型:不依赖于任何计算机系统和DBMS的数据模型
  • \n
  • 逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计
  • \n
\n
\n

组成要素:

\n
    \n
  • 数据结构
  • \n
  • 数据操作
  • \n
  • 数据完整性的约束条件
  • \n
\n
\n

概念模型

概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:实体-联系方法(E-R方法)

\n

实体联系模型

    \n
  • 实体(Entity):客观世界事物的集合,如学生、教师……
  • \n
  • 属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号……
  • \n
  • 码(Key):唯一标识实体的属性集合,如学号
  • \n
  • 域(Domain):属性的取值范围,如性别有男、女
  • \n
\n
\n
    \n
  • 实体型

    \n
  • \n
  • 实体集

    \n
  • \n
  • 联系
  • \n
\n
\n

实体联系类型

    \n
  • 一对一(1:1)
  • \n
  • 一对多(1:N)
  • \n
  • 多对多(M:N)
  • \n
\n

常用数据模型

层次模型

树形结构来表示各类实体以及实体间的联系的数据模型

\n
    \n
  1. 数据结构
      \n
    • 有且只有一个结点,无父结点,该结点为根结点
    • \n
    • 根结点外的其他结点有且仅有一个父结点
    • \n
    \n
  2. \n
  3. 表现形式(以多对多为例)
      \n
    • 冗余结点法
    • \n
    • 虚拟结点法
    • \n
    \n
  4. \n
  5. 数据操作和完整性约束
      \n
    • 数据操作:增删查改
    • \n
    • 完整性约束
    • \n
    \n
  6. \n
  7. 数据存储形式
      \n
    • 邻接法
    • \n
    • 链接法
    • \n
    \n
  8. \n
  9. 优缺点
      \n
    • 优点:结构简单,查询效率高
    • \n
    • 缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发
    • \n
    \n
  10. \n
\n

网状模型

网状结构来表示各类实体之间的联系的数据模型

\n
    \n
  1. 数据结构
      \n
    • 允许一个以上结点无双亲
    • \n
    • 一个结点可以有多于一个的双亲
    • \n
    \n
  2. \n
  3. 优缺点:
      \n
    • 优点:描述方便,存储效率高
    • \n
    • 缺点:结构复杂,处理复杂
    • \n
    \n
  4. \n
\n

关系模型

关系来表示实体与实体间的联系

\n
    \n
  1. 数据结构

    \n
      \n
    • 一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系
    • \n
    • 元组:表的一行称为元组,一个元组可以表示一个实体或实体间的联系
    • \n
    • 属性:表中的一列称为关系的一个属性
    • \n
    • 分量:元组中的一个属性值
    • \n
    \n
  2. \n
\n

\"image-20210323234510823\"

\n
    \n
  1. 数据操作:增删查改
  2. \n
  3. 完整性约束:实体完整性,参照完整性、用户自定义完整性
  4. \n
\n

系统结构

二级映像功能

    \n
  • 模式/内模式映像
  • \n
  • 外模式/模式映像
  • \n
\n

数据库组成

    \n
  1. 数据库
  2. \n
  3. DBMS
  4. \n
  5. 应用程序
  6. \n
  7. 数据库管理员(DBA)
  8. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

数据库介绍与认识

数据库管理系统:DBMS(database manage system)

\n

数据模型

    \n
  • 概念模型:不依赖于任何计算机系统和DBMS的数据模型
  • \n
  • 逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计
  • \n
\n
\n

组成要素:

\n
    \n
  • 数据结构
  • \n
  • 数据操作
  • \n
  • 数据完整性的约束条件
  • \n
\n
\n

概念模型

概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:实体-联系方法(E-R方法)

\n

实体联系模型

    \n
  • 实体(Entity):客观世界事物的集合,如学生、教师……
  • \n
  • 属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号……
  • \n
  • 码(Key):唯一标识实体的属性集合,如学号
  • \n
  • 域(Domain):属性的取值范围,如性别有男、女
  • \n
\n
\n
    \n
  • 实体型

    \n
  • \n
  • 实体集

    \n
  • \n
  • 联系
  • \n
\n
\n

实体联系类型

    \n
  • 一对一(1:1)
  • \n
  • 一对多(1:N)
  • \n
  • 多对多(M:N)
  • \n
\n

常用数据模型

层次模型

树形结构来表示各类实体以及实体间的联系的数据模型

\n
    \n
  1. 数据结构
      \n
    • 有且只有一个结点,无父结点,该结点为根结点
    • \n
    • 根结点外的其他结点有且仅有一个父结点
    • \n
    \n
  2. \n
  3. 表现形式(以多对多为例)
      \n
    • 冗余结点法
    • \n
    • 虚拟结点法
    • \n
    \n
  4. \n
  5. 数据操作和完整性约束
      \n
    • 数据操作:增删查改
    • \n
    • 完整性约束
    • \n
    \n
  6. \n
  7. 数据存储形式
      \n
    • 邻接法
    • \n
    • 链接法
    • \n
    \n
  8. \n
  9. 优缺点
      \n
    • 优点:结构简单,查询效率高
    • \n
    • 缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发
    • \n
    \n
  10. \n
\n

网状模型

网状结构来表示各类实体之间的联系的数据模型

\n
    \n
  1. 数据结构
      \n
    • 允许一个以上结点无双亲
    • \n
    • 一个结点可以有多于一个的双亲
    • \n
    \n
  2. \n
  3. 优缺点:
      \n
    • 优点:描述方便,存储效率高
    • \n
    • 缺点:结构复杂,处理复杂
    • \n
    \n
  4. \n
\n

关系模型

关系来表示实体与实体间的联系

\n
    \n
  1. 数据结构

    \n
      \n
    • 一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系
    • \n
    • 元组:表的一行称为元组,一个元组可以表示一个实体或实体间的联系
    • \n
    • 属性:表中的一列称为关系的一个属性
    • \n
    • 分量:元组中的一个属性值
    • \n
    \n
  2. \n
\n

\"image-20210323234510823\"

\n
    \n
  1. 数据操作:增删查改
  2. \n
  3. 完整性约束:实体完整性,参照完整性、用户自定义完整性
  4. \n
\n

系统结构

二级映像功能

    \n
  • 模式/内模式映像
  • \n
  • 外模式/模式映像
  • \n
\n

数据库组成

    \n
  1. 数据库
  2. \n
  3. DBMS
  4. \n
  5. 应用程序
  6. \n
  7. 数据库管理员(DBA)
  8. \n
\n"},{"title":"MySql(4)-MySQL事务","abbrlink":"2a3cf99e","date":"2021-06-08T02:51:44.000Z","description":"MySQL事务原则(原子性、一致性、持久性、隔离性)、事务执行","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# 事务\n\n## 什么是事务\n\n什么是事务:要么都成功,要么都失败\n\n> 比如:\n>\n> a 转账给 b 100元\n>\n> b 收到 a 的钱 100元\n>\n> 这两者应该:要么都成功,要么都失败\n\n\n\n事务原则:ACID原则->【原子性、一致性、隔离性、持久性】\n\n- 原子性:要么都成功,要么都失败\n- 一致性:事务前后(时间上)数据完整性一致\n- 持久性:事务一旦提交则不可逆\n- 隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰\n\n\n\n> 隔离导致的一些问题:\n>\n> - 脏读:一个事务读取另一个事务未提交的数据\n> - 不可重复读:在一个事务内读取表中某一数据,多次读取结果不同\n> - 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致\n\n\n\n\n\n## 事务执行\n\nMySQL默认开启事务自动提交\n\n手动处理事务:\n\n```mysql\nset autocommit = 0 -- 关闭事务自动提交\nstart transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内\n\n/**\n * 数据操作\n */\n\n-- 提交:持久化(成功)\ncommit\n\n-- 回滚:回到原来样子(事务失败)\nrollback\n\n-- 事务结束\nset autocommit = 1 -- 恢复自动提交\n\nsavepoint 保存点名 ; -- 设置事务保存点\nrollback to savepoint 保存点名 ; -- 回到事务保存点\nrelease savepoint 保存点名; -- 撤销保存点\n\n\n\n```\n\n\n\n","source":"_posts/MySQL/4-MySQL事务.md","raw":"---\ntitle: MySql(4)-MySQL事务\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 2a3cf99e\ndate: 2021-06-08 10:51:44\ndescription: MySQL事务原则(原子性、一致性、持久性、隔离性)、事务执行\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# 事务\n\n## 什么是事务\n\n什么是事务:要么都成功,要么都失败\n\n> 比如:\n>\n> a 转账给 b 100元\n>\n> b 收到 a 的钱 100元\n>\n> 这两者应该:要么都成功,要么都失败\n\n\n\n事务原则:ACID原则->【原子性、一致性、隔离性、持久性】\n\n- 原子性:要么都成功,要么都失败\n- 一致性:事务前后(时间上)数据完整性一致\n- 持久性:事务一旦提交则不可逆\n- 隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰\n\n\n\n> 隔离导致的一些问题:\n>\n> - 脏读:一个事务读取另一个事务未提交的数据\n> - 不可重复读:在一个事务内读取表中某一数据,多次读取结果不同\n> - 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致\n\n\n\n\n\n## 事务执行\n\nMySQL默认开启事务自动提交\n\n手动处理事务:\n\n```mysql\nset autocommit = 0 -- 关闭事务自动提交\nstart transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内\n\n/**\n * 数据操作\n */\n\n-- 提交:持久化(成功)\ncommit\n\n-- 回滚:回到原来样子(事务失败)\nrollback\n\n-- 事务结束\nset autocommit = 1 -- 恢复自动提交\n\nsavepoint 保存点名 ; -- 设置事务保存点\nrollback to savepoint 保存点名 ; -- 回到事务保存点\nrelease savepoint 保存点名; -- 撤销保存点\n\n\n\n```\n\n\n\n","slug":"MySQL/4-MySQL事务","published":1,"updated":"2021-09-08T13:06:03.208Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oq001jakvebouthuqu","content":"

事务

什么是事务

什么是事务:要么都成功,要么都失败

\n
\n

比如:

\n

a 转账给 b 100元

\n

b 收到 a 的钱 100元

\n

这两者应该:要么都成功,要么都失败

\n
\n

事务原则:ACID原则->【原子性、一致性、隔离性、持久性】

\n
    \n
  • 原子性:要么都成功,要么都失败
  • \n
  • 一致性:事务前后(时间上)数据完整性一致
  • \n
  • 持久性:事务一旦提交则不可逆
  • \n
  • 隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰
  • \n
\n
\n

隔离导致的一些问题:

\n
    \n
  • 脏读:一个事务读取另一个事务未提交的数据
  • \n
  • 不可重复读:在一个事务内读取表中某一数据,多次读取结果不同
  • \n
  • 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致
  • \n
\n
\n

事务执行

MySQL默认开启事务自动提交

\n

手动处理事务:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
set autocommit = 0 -- 关闭事务自动提交
start transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内

/**
* 数据操作
*/

-- 提交:持久化(成功)
commit

-- 回滚:回到原来样子(事务失败)
rollback

-- 事务结束
set autocommit = 1 -- 恢复自动提交

savepoint 保存点名 ; -- 设置事务保存点
rollback to savepoint 保存点名 ; -- 回到事务保存点
release savepoint 保存点名; -- 撤销保存点



\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

事务

什么是事务

什么是事务:要么都成功,要么都失败

\n
\n

比如:

\n

a 转账给 b 100元

\n

b 收到 a 的钱 100元

\n

这两者应该:要么都成功,要么都失败

\n
\n

事务原则:ACID原则->【原子性、一致性、隔离性、持久性】

\n
    \n
  • 原子性:要么都成功,要么都失败
  • \n
  • 一致性:事务前后(时间上)数据完整性一致
  • \n
  • 持久性:事务一旦提交则不可逆
  • \n
  • 隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰
  • \n
\n
\n

隔离导致的一些问题:

\n
    \n
  • 脏读:一个事务读取另一个事务未提交的数据
  • \n
  • 不可重复读:在一个事务内读取表中某一数据,多次读取结果不同
  • \n
  • 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致
  • \n
\n
\n

事务执行

MySQL默认开启事务自动提交

\n

手动处理事务:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
set autocommit = 0 -- 关闭事务自动提交
start transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内

/**
* 数据操作
*/

-- 提交:持久化(成功)
commit

-- 回滚:回到原来样子(事务失败)
rollback

-- 事务结束
set autocommit = 1 -- 恢复自动提交

savepoint 保存点名 ; -- 设置事务保存点
rollback to savepoint 保存点名 ; -- 回到事务保存点
release savepoint 保存点名; -- 撤销保存点



\n"},{"title":"MySql(3)-数据管理","abbrlink":"d709dda","date":"2021-06-07T02:43:14.000Z","description":"Mysql数据管理,增删查改","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# MySQL数据管理\n\n## 关系操作符\n\n| 运算符 | 含义 | 例子 | 结果 |\n| :------: | :----: | :--: | :---: |\n| = | 等于 | 5=6 | false |\n| <> 或 != | 不等于 | 5<>6 | true |\n| > | | | |\n| < | | | |\n| <= | | | |\n| >= | | | |\n| !< | 不小于 | | |\n| !> | 不大于 | | |\n\n\n\n## 主键\n\n主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。\n\n\n\n在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,`students`表的两行记录:\n\n| id | class_id | name | gender | score |\n| :--: | :------: | :--: | :----: | :---: |\n| 1 | 1 | 小明 | M | 90 |\n| 2 | 1 | 小红 | F | 95 |\n\n每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。\n\n假设我们把`name`字段作为主键,那么通过名字`小明`或`小红`就能唯一确定一条记录。但是,这么设定,就没法存储**同名**的同学了,因为插入相同主键的两条记录是不被允许的。\n\n\n\n对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。\n\n\n\n### 联合主键\n\n关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。\n\n对于联合主键,允许一列有重复,只要不是所有主键列都重复即可\n\n\n\n\n\n## 外键(了解)\n\n\n\n当我们用主键唯一标识记录时,我们就可以在`students`表中确定任意一个学生的记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 小明 | ... |\n| 2 | 小红 | ... |\n\n我们还可以在`classes`表中确定任意一个班级记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 一班 | ... |\n| 2 | 二班 | ... |\n\n但是我们如何确定`students`表的一条记录,例如,`id=1`的小明,属于哪个班级呢?\n\n\n\n由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个`classes`的记录可以对应多个`students`表的记录。\n\n\n\n为了表达这种一对多的关系,我们需要在`students`表中加入一列`class_id`,让它的值与`classes`表的某条记录相对应:\n\n| id | class_id | name | other columns... |\n| :--- | :------- | :--- | :--------------- |\n| 1 | 1 | 小明 | ... |\n| 2 | 1 | 小红 | ... |\n| 5 | 2 | 小白 | ... |\n\n这样,我们就可以根据`class_id`这个列直接定位出一个`students`表的记录应该对应到`classes`的哪条记录。\n\n\n\n\n\n给一张表 `students` 添加外键约束:\n\n```SQL\nALTER TABLE students\nADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意\nFOREIGN KEY (class_id) # 指定了`class_id`作为外键\nREFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)\n```\n\n\n\n\n\n添加了外键的表(`students`)为从表,被外键引用的表(`classes`)为主表,要删除主表前需先删除从表\n\n> 注意:以上外键是数据库级别外键,不推荐使用\n>\n> 最佳实现:\n>\n> - 数据库就是单纯的表,只有行(记录)、列(字段)\n> - 想使用多张表数据,想用外键(用程序如Java语言实现)\n\n\n\n## DML语言(全部记住)\n\n即数据操作语言\n\n- 插入(增)一行(记录):insert into...values...\n\n```mysql\ninsert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...;\n\n例:\n\nINSERT INTO `student` ( `id`, `name` )\nVALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');\n```\n\n![](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-26-08_image-20210419143255520.png)\n\n- 删除一列(字段):\n - delete\n - truncate\n - 相同:都会删除记录,表的结构和索引、约束不会变\n - 区别:\n 1. truncate 重新设置自增列,计数器会归零;delete不会影响自增\n 2. truncate 不会影响事务\n\n```mysql\n-- 删除一条记录\ndelete from `student` where id = '1001';\n\n-- 删除所有记录(不建议这样写)\ndelete from `student`;\n\n```\n\n```mysql\n-- 清空表的所有记录,但表的结构和索引不变\ntruncate table `student`;\n```\n\n> 对不同引擎的数据库使用`delete`语句后,重启数据库,现象:(了解)\n>\n> - InnoDB:自增列会从1开始(因为保存在内存中)\n> - MyISAM:继续从上一个自增量开始(保存在文件中)\n\n\n\n\n\n- 修改:update...set...\n\n```mysql\n-- 用where指定条件\nupdate `student` set `name`='wangwu' where `id` = `1001`;\n\n-- 不指定条件情况下,会改动所有行(即记录)\nupdate `student` set `name`='wangwu';\n\n-- 修改多个字段, 用逗号隔开\nupdate `student` set `name`='wangwu', `age`=20 where `id` = `1001`;\n\n-- 通过多个条件定位数据, 用逻辑运算符and、or...\nupdate `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;\n```\n\n> 注意 **【=】** 含义:\n>\n> 在set这样的语句后面表示赋值;\n>\n> 在where后面表示关系运算符,表示比较;\n\n\n\n## DQL查询数据(最重要)\n\nDQL(data query language:数据查询语言)\n\n- 所有查询(简单、复杂)都用它\n- 数据库中最核心、重要的语句\n- 使用频率最高的语句\n\n### 基本查询操作 select\n\n1. 查询字段\n\n ```mysql\n -- 查询全部字段 select 字段 from 表\n select * from `student`;\n \n -- 查询指定字段 select 字段1[, 字段2, ...] from 表;\n select name, age from student;\n \n -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写\n select name as `学生姓名`, age as `年龄` from `student` as `s`;\n \n -- 函数concat(a, b): 连接a、b字符串\n select concat('姓名:', name) as 新名字 from student;\n ```\n\n \n\n2. 去重 `distinct`\n\n ```mysql\n -- 去除select后重复的数据,只留下一条\n select distinct `age` from student;\n ```\n\n3. 其他\n\n ```mysql\n -- 查询系统版本\n select version(); -- version()是个函数\n \n -- 计算\n select 100*3-1 as 计算结果;\n \n -- 所有学员考试成绩 +1 分\n select `name`, `grade` + 1 as 提成1分后 from `student`; \n \n -- 查询系统内置变量值\n select @@auto_increment_increment; -- 查询自增步长\n \n ```\n\n数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。\n\n```mysql\nselect 表达式 from 表;\n```\n\n\n\n### where语句\n\n检索数据中符合条件的值\n\n| 逻辑运算符 | 语法 | 描述 |\n| :--------- | :------------------ | :-------------- |\n| and `&&` | a and b、 `a&&b` | 2个都为真返回真 |\n| or `||` | a or b 、 `a||b` | 。。。 |\n| not `!` | not a、 `!a` | 。。。 |\n\n```mysql\nSELECT\n\tid AS 学号,\n\t`name` AS 姓名 \nFROM\n\tstudent \nWHERE\n\tgrade > 80 \n\tAND age < 21;\n```\n\n```mysql\nSELECT\n\t`id` AS 学号,\n\t`name` AS 姓名,\n\t`grade` as 成绩\nFROM\n\tstudent \nWHERE\n\tgrade BETWEEN 70 AND 90;\n```\n\n### 模糊查询\n\n| 运算符 | 语法 | 描述 |\n| ----------- | --------------------- | --------------------------------------- |\n| is null | a is null | 如果a是null,则为真 |\n| is not null | a is not null | 如果a不是null,则为真 |\n| between | a between b and c | 若a在b和c之间,结果为真 |\n| like | a like b | sql匹配,若a匹配b,结果为真 |\n| in | a in(a1,a2,a3,a4,...) | 若a是a1,a2,a3,a4...中的一个值,结果为真 |\n\nlike:通常结合 `%`、`_` 来使用\n\n```mysql\n-- 查找以王字开头的name \n-- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王%'; -- 表示以【王】字开头\n\n\n-- 查找以王字开头且【名字为2个字】的name\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王__'; -- 两个下划线 \n\n\n-- 查找生日不是null的\nSELECT `id`, `name` FROM student\nWHERE `birthday` is not null;\n```\n\n\n\n### 联表查询 join on\n\n同时在2个表中查找数据(必须要有交集)\n\n\n\n\"img\"\n\n\n\n```mysql\n/**\n有2张表(都有id、name字段):\n1. `student`:班级学生基本信息\n2. `exam`:年级学生考试信息\n\n【问题:查找出本班级学生的考试信息】\n*/\n\n-- =====================================\n-- 取交集 inner join\nselect s.id as 学号, s.name as 姓名 \nfrom student as s inner join exam as e -- as可以省略\non s.id = e.id;\n\n-- =====================================\n-- 仅左边的表有,left join \nSELECT\n\ts.id 学号, -- 省略as\n\ts.NAME 姓名 \nFROM\n\tstudent s\n\tLEFT JOIN exam e ON s.NAME = e.NAME \nWHERE\n\te.`name` IS NULL;\n-- =====================================\n\n-- 两个表的所有人 \n\n```\n\n> mysql 不支持 full outer join\n>\n\n\n\n\n\n### 自连接及联表查询\n\n自己的表和自己的表连接,核心:1张表拆为2张一样的表\n\n\n\n### 分页和排序\n\n- 排序 **order by**\n\n - 升序用 ASC, 降序用DESC\n\n - order by 表示查询结果怎么排(升序、降序),通过哪个字段排\n\n 比如:\n\n ```mysql\n -- order 字段 ASC 升序\n -- order 字段 DESC 降序\n SELECT * FROM exam ORDER BY `grade` ASC; -- 成绩按升序排\n ```\n\n- 分页 **limit**:可以缓解数据库压力、给人体验更好、瀑布流\n\n - 语法:\n\n ```mysql\n -- limit 起始下标(从0开始) 页面大小\n \n -- 从第0条数据开始,显示2条数据\n SELECT * FROM exam ORDER BY `id` DESC LIMIT 0,2;\n ```\n\n \n\n\n\n### 子查询与嵌套查询\n\nwhere 表达式【这个值是计算出来的】\n\n即:where 后面还跟select语句\n\n\n\n## MySQL常用函数\n\n### 数学运算\n\n```mysql\nabs()\n\nceiling() -- 向上取整 9.1 -> 10\nfloor() -- 向下取整 9.1 -> 9\n\nrand() -- 返回0~1随机数\n\nsign() -- 判断一个数符号 0->0 负数->-1 正数->1\n```\n\n\n\n### 字符串\n\n```mysql\nchar_length() -- 字符串长度\n\nconcat('a', 'b', 'c') -- 字符串连接 返回abc\n\n-- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2\ninsert(str1, n1, len, str2)\n-- 如\nselect insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef\n\n-- 将一个字符串str1中的指定子字符串substr替换为新字符串str2\nreplace(str1, substr, str2)\n\n-- 截取指定字符串\nsubstr(str, a, len); -- 截取str中第a个开始,共len个字符的子串\n\n-- 大小写转换\nupper()\nlower()\n\n-- 返回在str第一次出现的字符串substr的索引\ninstr(str, substr) -- 没有则返回0\n\n-- 反转字符串\nreverse(str)\n\n```\n\n### 时间-日期函数\n\n```mysql\n-- 当前日期\nSELECT CURRENT_DATE()\nSELECT CURDATE()\n\nSELECT NOW(); -- 当前时间 2021-04-20 20:05:44\nSELECT LOCALTIME(); -- 本地时间\nSELECT SYSDATE(); -- 系统时间\n\nSELECT YEAR(NOW()); -- 年\nSELECT MONTH(NOW()); -- 月\nSELECT DAY(NOW());\t-- 日\nSELECT HOUR(NOW());\t-- 时\nSELECT MINUTE(NOW()); -- 分\nSELECT SECOND(NOW()); -- 秒\n```\n\n\n\n### 系统函数\n\n```mysql\nSELECT SYSTEM_USER(); -- 当前系统用户\nSELECT USER()\t\t\t-- 当前用户\nSELECT VERSION()\t\t-- 版本号\n```\n\n\n\n### 聚合函数(常用)\n\n```mysql\ncount() -- 计数有多少个记录\n\tselect count(字段) from tablename; -- 忽略所有null值\n\tselect count(*) from tablename; -- 不会忽略null值,计算行数\n\tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同\n\t\nsum()\n\tselect sum(字段) from tablename;\n\t\navg()\n\tselect avg(字段) from tablename;\n\nmax()\n\tselect max(字段) from tablename;\n\nmin()\n\tselect min(字段) from tablename;\n```\n\n\n\n\n\n> **select**语法总结\n>\n> ![image-20210420202906345](https://gitee.com/ajream/images/raw/master/img/2021-04-20 20-29-07_image-20210420202906345.png)\n\n\n\n## 数据库级别的md5摘要算法\n\n**MD5消息摘要算法**(英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】\n\nmd5不可逆,具体值的md5值是一样的\n\nmd5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样\n\nSQL使用函数 `MD5()`进行加密\n\n\n\n","source":"_posts/MySQL/3-数据管理.md","raw":"---\ntitle: MySql(3)-数据管理\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: d709dda\ndate: 2021-06-07 10:43:14\ndescription: Mysql数据管理,增删查改\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# MySQL数据管理\n\n## 关系操作符\n\n| 运算符 | 含义 | 例子 | 结果 |\n| :------: | :----: | :--: | :---: |\n| = | 等于 | 5=6 | false |\n| <> 或 != | 不等于 | 5<>6 | true |\n| > | | | |\n| < | | | |\n| <= | | | |\n| >= | | | |\n| !< | 不小于 | | |\n| !> | 不大于 | | |\n\n\n\n## 主键\n\n主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。\n\n\n\n在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,`students`表的两行记录:\n\n| id | class_id | name | gender | score |\n| :--: | :------: | :--: | :----: | :---: |\n| 1 | 1 | 小明 | M | 90 |\n| 2 | 1 | 小红 | F | 95 |\n\n每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。\n\n假设我们把`name`字段作为主键,那么通过名字`小明`或`小红`就能唯一确定一条记录。但是,这么设定,就没法存储**同名**的同学了,因为插入相同主键的两条记录是不被允许的。\n\n\n\n对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。\n\n\n\n### 联合主键\n\n关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。\n\n对于联合主键,允许一列有重复,只要不是所有主键列都重复即可\n\n\n\n\n\n## 外键(了解)\n\n\n\n当我们用主键唯一标识记录时,我们就可以在`students`表中确定任意一个学生的记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 小明 | ... |\n| 2 | 小红 | ... |\n\n我们还可以在`classes`表中确定任意一个班级记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 一班 | ... |\n| 2 | 二班 | ... |\n\n但是我们如何确定`students`表的一条记录,例如,`id=1`的小明,属于哪个班级呢?\n\n\n\n由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个`classes`的记录可以对应多个`students`表的记录。\n\n\n\n为了表达这种一对多的关系,我们需要在`students`表中加入一列`class_id`,让它的值与`classes`表的某条记录相对应:\n\n| id | class_id | name | other columns... |\n| :--- | :------- | :--- | :--------------- |\n| 1 | 1 | 小明 | ... |\n| 2 | 1 | 小红 | ... |\n| 5 | 2 | 小白 | ... |\n\n这样,我们就可以根据`class_id`这个列直接定位出一个`students`表的记录应该对应到`classes`的哪条记录。\n\n\n\n\n\n给一张表 `students` 添加外键约束:\n\n```SQL\nALTER TABLE students\nADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意\nFOREIGN KEY (class_id) # 指定了`class_id`作为外键\nREFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)\n```\n\n\n\n\n\n添加了外键的表(`students`)为从表,被外键引用的表(`classes`)为主表,要删除主表前需先删除从表\n\n> 注意:以上外键是数据库级别外键,不推荐使用\n>\n> 最佳实现:\n>\n> - 数据库就是单纯的表,只有行(记录)、列(字段)\n> - 想使用多张表数据,想用外键(用程序如Java语言实现)\n\n\n\n## DML语言(全部记住)\n\n即数据操作语言\n\n- 插入(增)一行(记录):insert into...values...\n\n```mysql\ninsert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...;\n\n例:\n\nINSERT INTO `student` ( `id`, `name` )\nVALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');\n```\n\n![](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-26-08_image-20210419143255520.png)\n\n- 删除一列(字段):\n - delete\n - truncate\n - 相同:都会删除记录,表的结构和索引、约束不会变\n - 区别:\n 1. truncate 重新设置自增列,计数器会归零;delete不会影响自增\n 2. truncate 不会影响事务\n\n```mysql\n-- 删除一条记录\ndelete from `student` where id = '1001';\n\n-- 删除所有记录(不建议这样写)\ndelete from `student`;\n\n```\n\n```mysql\n-- 清空表的所有记录,但表的结构和索引不变\ntruncate table `student`;\n```\n\n> 对不同引擎的数据库使用`delete`语句后,重启数据库,现象:(了解)\n>\n> - InnoDB:自增列会从1开始(因为保存在内存中)\n> - MyISAM:继续从上一个自增量开始(保存在文件中)\n\n\n\n\n\n- 修改:update...set...\n\n```mysql\n-- 用where指定条件\nupdate `student` set `name`='wangwu' where `id` = `1001`;\n\n-- 不指定条件情况下,会改动所有行(即记录)\nupdate `student` set `name`='wangwu';\n\n-- 修改多个字段, 用逗号隔开\nupdate `student` set `name`='wangwu', `age`=20 where `id` = `1001`;\n\n-- 通过多个条件定位数据, 用逻辑运算符and、or...\nupdate `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;\n```\n\n> 注意 **【=】** 含义:\n>\n> 在set这样的语句后面表示赋值;\n>\n> 在where后面表示关系运算符,表示比较;\n\n\n\n## DQL查询数据(最重要)\n\nDQL(data query language:数据查询语言)\n\n- 所有查询(简单、复杂)都用它\n- 数据库中最核心、重要的语句\n- 使用频率最高的语句\n\n### 基本查询操作 select\n\n1. 查询字段\n\n ```mysql\n -- 查询全部字段 select 字段 from 表\n select * from `student`;\n \n -- 查询指定字段 select 字段1[, 字段2, ...] from 表;\n select name, age from student;\n \n -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写\n select name as `学生姓名`, age as `年龄` from `student` as `s`;\n \n -- 函数concat(a, b): 连接a、b字符串\n select concat('姓名:', name) as 新名字 from student;\n ```\n\n \n\n2. 去重 `distinct`\n\n ```mysql\n -- 去除select后重复的数据,只留下一条\n select distinct `age` from student;\n ```\n\n3. 其他\n\n ```mysql\n -- 查询系统版本\n select version(); -- version()是个函数\n \n -- 计算\n select 100*3-1 as 计算结果;\n \n -- 所有学员考试成绩 +1 分\n select `name`, `grade` + 1 as 提成1分后 from `student`; \n \n -- 查询系统内置变量值\n select @@auto_increment_increment; -- 查询自增步长\n \n ```\n\n数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。\n\n```mysql\nselect 表达式 from 表;\n```\n\n\n\n### where语句\n\n检索数据中符合条件的值\n\n| 逻辑运算符 | 语法 | 描述 |\n| :--------- | :------------------ | :-------------- |\n| and `&&` | a and b、 `a&&b` | 2个都为真返回真 |\n| or `||` | a or b 、 `a||b` | 。。。 |\n| not `!` | not a、 `!a` | 。。。 |\n\n```mysql\nSELECT\n\tid AS 学号,\n\t`name` AS 姓名 \nFROM\n\tstudent \nWHERE\n\tgrade > 80 \n\tAND age < 21;\n```\n\n```mysql\nSELECT\n\t`id` AS 学号,\n\t`name` AS 姓名,\n\t`grade` as 成绩\nFROM\n\tstudent \nWHERE\n\tgrade BETWEEN 70 AND 90;\n```\n\n### 模糊查询\n\n| 运算符 | 语法 | 描述 |\n| ----------- | --------------------- | --------------------------------------- |\n| is null | a is null | 如果a是null,则为真 |\n| is not null | a is not null | 如果a不是null,则为真 |\n| between | a between b and c | 若a在b和c之间,结果为真 |\n| like | a like b | sql匹配,若a匹配b,结果为真 |\n| in | a in(a1,a2,a3,a4,...) | 若a是a1,a2,a3,a4...中的一个值,结果为真 |\n\nlike:通常结合 `%`、`_` 来使用\n\n```mysql\n-- 查找以王字开头的name \n-- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王%'; -- 表示以【王】字开头\n\n\n-- 查找以王字开头且【名字为2个字】的name\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王__'; -- 两个下划线 \n\n\n-- 查找生日不是null的\nSELECT `id`, `name` FROM student\nWHERE `birthday` is not null;\n```\n\n\n\n### 联表查询 join on\n\n同时在2个表中查找数据(必须要有交集)\n\n\n\n\"img\"\n\n\n\n```mysql\n/**\n有2张表(都有id、name字段):\n1. `student`:班级学生基本信息\n2. `exam`:年级学生考试信息\n\n【问题:查找出本班级学生的考试信息】\n*/\n\n-- =====================================\n-- 取交集 inner join\nselect s.id as 学号, s.name as 姓名 \nfrom student as s inner join exam as e -- as可以省略\non s.id = e.id;\n\n-- =====================================\n-- 仅左边的表有,left join \nSELECT\n\ts.id 学号, -- 省略as\n\ts.NAME 姓名 \nFROM\n\tstudent s\n\tLEFT JOIN exam e ON s.NAME = e.NAME \nWHERE\n\te.`name` IS NULL;\n-- =====================================\n\n-- 两个表的所有人 \n\n```\n\n> mysql 不支持 full outer join\n>\n\n\n\n\n\n### 自连接及联表查询\n\n自己的表和自己的表连接,核心:1张表拆为2张一样的表\n\n\n\n### 分页和排序\n\n- 排序 **order by**\n\n - 升序用 ASC, 降序用DESC\n\n - order by 表示查询结果怎么排(升序、降序),通过哪个字段排\n\n 比如:\n\n ```mysql\n -- order 字段 ASC 升序\n -- order 字段 DESC 降序\n SELECT * FROM exam ORDER BY `grade` ASC; -- 成绩按升序排\n ```\n\n- 分页 **limit**:可以缓解数据库压力、给人体验更好、瀑布流\n\n - 语法:\n\n ```mysql\n -- limit 起始下标(从0开始) 页面大小\n \n -- 从第0条数据开始,显示2条数据\n SELECT * FROM exam ORDER BY `id` DESC LIMIT 0,2;\n ```\n\n \n\n\n\n### 子查询与嵌套查询\n\nwhere 表达式【这个值是计算出来的】\n\n即:where 后面还跟select语句\n\n\n\n## MySQL常用函数\n\n### 数学运算\n\n```mysql\nabs()\n\nceiling() -- 向上取整 9.1 -> 10\nfloor() -- 向下取整 9.1 -> 9\n\nrand() -- 返回0~1随机数\n\nsign() -- 判断一个数符号 0->0 负数->-1 正数->1\n```\n\n\n\n### 字符串\n\n```mysql\nchar_length() -- 字符串长度\n\nconcat('a', 'b', 'c') -- 字符串连接 返回abc\n\n-- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2\ninsert(str1, n1, len, str2)\n-- 如\nselect insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef\n\n-- 将一个字符串str1中的指定子字符串substr替换为新字符串str2\nreplace(str1, substr, str2)\n\n-- 截取指定字符串\nsubstr(str, a, len); -- 截取str中第a个开始,共len个字符的子串\n\n-- 大小写转换\nupper()\nlower()\n\n-- 返回在str第一次出现的字符串substr的索引\ninstr(str, substr) -- 没有则返回0\n\n-- 反转字符串\nreverse(str)\n\n```\n\n### 时间-日期函数\n\n```mysql\n-- 当前日期\nSELECT CURRENT_DATE()\nSELECT CURDATE()\n\nSELECT NOW(); -- 当前时间 2021-04-20 20:05:44\nSELECT LOCALTIME(); -- 本地时间\nSELECT SYSDATE(); -- 系统时间\n\nSELECT YEAR(NOW()); -- 年\nSELECT MONTH(NOW()); -- 月\nSELECT DAY(NOW());\t-- 日\nSELECT HOUR(NOW());\t-- 时\nSELECT MINUTE(NOW()); -- 分\nSELECT SECOND(NOW()); -- 秒\n```\n\n\n\n### 系统函数\n\n```mysql\nSELECT SYSTEM_USER(); -- 当前系统用户\nSELECT USER()\t\t\t-- 当前用户\nSELECT VERSION()\t\t-- 版本号\n```\n\n\n\n### 聚合函数(常用)\n\n```mysql\ncount() -- 计数有多少个记录\n\tselect count(字段) from tablename; -- 忽略所有null值\n\tselect count(*) from tablename; -- 不会忽略null值,计算行数\n\tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同\n\t\nsum()\n\tselect sum(字段) from tablename;\n\t\navg()\n\tselect avg(字段) from tablename;\n\nmax()\n\tselect max(字段) from tablename;\n\nmin()\n\tselect min(字段) from tablename;\n```\n\n\n\n\n\n> **select**语法总结\n>\n> ![image-20210420202906345](https://gitee.com/ajream/images/raw/master/img/2021-04-20 20-29-07_image-20210420202906345.png)\n\n\n\n## 数据库级别的md5摘要算法\n\n**MD5消息摘要算法**(英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】\n\nmd5不可逆,具体值的md5值是一样的\n\nmd5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样\n\nSQL使用函数 `MD5()`进行加密\n\n\n\n","slug":"MySQL/3-数据管理","published":1,"updated":"2021-09-10T03:01:55.065Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6or001nakved1j01zfv","content":"

MySQL数据管理

关系操作符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符含义例子结果
=等于5=6false
<> 或 !=不等于5<>6true
>
<
<=
>=
!<不小于
!>不大于
\n
\n

主键

主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。

\n

在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,students表的两行记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idclass_idnamegenderscore
11小明M90
21小红F95
\n
\n

每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。

\n

假设我们把name字段作为主键,那么通过名字小明小红就能唯一确定一条记录。但是,这么设定,就没法存储同名的同学了,因为插入相同主键的两条记录是不被允许的。

\n

对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。

\n

联合主键

关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。

\n

对于联合主键,允许一列有重复,只要不是所有主键列都重复即可

\n

外键(了解)

当我们用主键唯一标识记录时,我们就可以在students表中确定任意一个学生的记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idnameother columns…
1小明
2小红
\n
\n

我们还可以在classes表中确定任意一个班级记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idnameother columns…
1一班
2二班
\n
\n

但是我们如何确定students表的一条记录,例如,id=1的小明,属于哪个班级呢?

\n

由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个classes的记录可以对应多个students表的记录。

\n

为了表达这种一对多的关系,我们需要在students表中加入一列class_id,让它的值与classes表的某条记录相对应:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idclass_idnameother columns…
11小明
21小红
52小白
\n
\n

这样,我们就可以根据class_id这个列直接定位出一个students表的记录应该对应到classes的哪条记录。

\n

给一张表 students 添加外键约束:

\n
1
2
3
4
ALTER TABLE students
ADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意
FOREIGN KEY (class_id) # 指定了`class_id`作为外键
REFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)
\n

添加了外键的表(students)为从表,被外键引用的表(classes)为主表,要删除主表前需先删除从表

\n
\n

注意:以上外键是数据库级别外键,不推荐使用

\n

最佳实现:

\n
    \n
  • 数据库就是单纯的表,只有行(记录)、列(字段)
  • \n
  • 想使用多张表数据,想用外键(用程序如Java语言实现)
  • \n
\n
\n

DML语言(全部记住)

即数据操作语言

\n
    \n
  • 插入(增)一行(记录):insert into…values…
  • \n
\n
1
2
3
4
5
6
insert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...;

例:

INSERT INTO `student` ( `id`, `name` )
VALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');
\n

\"\"

\n
    \n
  • 删除一列(字段):
      \n
    • delete
    • \n
    • truncate
    • \n
    • 相同:都会删除记录,表的结构和索引、约束不会变
    • \n
    • 区别:
        \n
      1. truncate 重新设置自增列,计数器会归零;delete不会影响自增
      2. \n
      3. truncate 不会影响事务
      4. \n
      \n
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
-- 删除一条记录
delete from `student` where id = '1001';

-- 删除所有记录(不建议这样写)
delete from `student`;

\n
1
2
-- 清空表的所有记录,但表的结构和索引不变
truncate table `student`;
\n
\n

对不同引擎的数据库使用delete语句后,重启数据库,现象:(了解)

\n
    \n
  • InnoDB:自增列会从1开始(因为保存在内存中)
  • \n
  • MyISAM:继续从上一个自增量开始(保存在文件中)
  • \n
\n
\n
    \n
  • 修改:update…set…
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
-- 用where指定条件
update `student` set `name`='wangwu' where `id` = `1001`;

-- 不指定条件情况下,会改动所有行(即记录)
update `student` set `name`='wangwu';

-- 修改多个字段, 用逗号隔开
update `student` set `name`='wangwu', `age`=20 where `id` = `1001`;

-- 通过多个条件定位数据, 用逻辑运算符and、or...
update `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;
\n
\n

注意 【=】 含义:

\n

在set这样的语句后面表示赋值;

\n

在where后面表示关系运算符,表示比较;

\n
\n

DQL查询数据(最重要)

DQL(data query language:数据查询语言)

\n
    \n
  • 所有查询(简单、复杂)都用它
  • \n
  • 数据库中最核心、重要的语句
  • \n
  • 使用频率最高的语句
  • \n
\n

基本查询操作 select

    \n
  1. 查询字段

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    -- 查询全部字段  select 字段 from 表
    select * from `student`;

    -- 查询指定字段 select 字段1[, 字段2, ...] from 表;
    select name, age from student;

    -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写
    select name as `学生姓名`, age as `年龄` from `student` as `s`;

    -- 函数concat(a, b): 连接a、b字符串
    select concat('姓名:', name) as 新名字 from student;
    \n
  2. \n
\n
    \n
  1. 去重 distinct

    \n
    1
    2
    -- 去除select后重复的数据,只留下一条
    select distinct `age` from student;
    \n
  2. \n
  3. 其他

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -- 查询系统版本
    select version(); -- version()是个函数

    -- 计算
    select 100*3-1 as 计算结果;

    -- 所有学员考试成绩 +1 分
    select `name`, `grade` + 1 as 提成1分后 from `student`;

    -- 查询系统内置变量值
    select @@auto_increment_increment; -- 查询自增步长

    \n
  4. \n
\n

数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。

\n
1
select 表达式 from 表;
\n

where语句

检索数据中符合条件的值

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
逻辑运算符语法描述
and &&a and b、 a&&b2个都为真返回真
or ``a or b 、 `ab`。。。
not !not a、 !a。。。
\n
\n
1
2
3
4
5
6
7
8
SELECT
\tid AS 学号,
\t`name` AS 姓名
FROM
\tstudent
WHERE
\tgrade > 80
\tAND age < 21;
\n
1
2
3
4
5
6
7
8
SELECT
\t`id` AS 学号,
\t`name` AS 姓名,
\t`grade` as 成绩
FROM
\tstudent
WHERE
\tgrade BETWEEN 70 AND 90;
\n

模糊查询

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符语法描述
is nulla is null如果a是null,则为真
is not nulla is not null如果a不是null,则为真
betweena between b and c若a在b和c之间,结果为真
likea like bsql匹配,若a匹配b,结果为真
ina in(a1,a2,a3,a4,…)若a是a1,a2,a3,a4…中的一个值,结果为真
\n
\n

like:通常结合 %_ 来使用

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 查找以王字开头的name  
-- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符
SELECT `id`, `name` FROM student
WHERE `name` LIKE '王%'; -- 表示以【王】字开头


-- 查找以王字开头且【名字为2个字】的name
SELECT `id`, `name` FROM student
WHERE `name` LIKE '王__'; -- 两个下划线


-- 查找生日不是null的
SELECT `id`, `name` FROM student
WHERE `birthday` is not null;
\n

联表查询 join on

同时在2个表中查找数据(必须要有交集)

\n

\"img\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
有2张表(都有id、name字段):
1. `student`:班级学生基本信息
2. `exam`:年级学生考试信息

【问题:查找出本班级学生的考试信息】
*/

-- =====================================
-- 取交集 inner join
select s.id as 学号, s.name as 姓名
from student as s inner join exam as e -- as可以省略
on s.id = e.id;

-- =====================================
-- 仅左边的表有,left join
SELECT
\ts.id 学号, -- 省略as
\ts.NAME 姓名
FROM
\tstudent s
\tLEFT JOIN exam e ON s.NAME = e.NAME
WHERE
\te.`name` IS NULL;
-- =====================================

-- 两个表的所有人

\n
\n

mysql 不支持 full outer join

\n
\n

自连接及联表查询

自己的表和自己的表连接,核心:1张表拆为2张一样的表

\n

分页和排序

    \n
  • 排序 order by

    \n
      \n
    • 升序用 ASC, 降序用DESC

      \n
    • \n
    • order by 表示查询结果怎么排(升序、降序),通过哪个字段排

      \n

      比如:

      \n
      1
      2
      3
      -- order 字段  ASC 升序
      -- order 字段 DESC 降序
      SELECT * FROM exam ORDER BY `grade` ASC; -- 成绩按升序排
      \n
    • \n
    \n
  • \n
  • 分页 limit:可以缓解数据库压力、给人体验更好、瀑布流

    \n
      \n
    • 语法:

      \n
      1
      2
      3
      4
      -- limit 起始下标(从0开始) 页面大小

      -- 从第0条数据开始,显示2条数据
      SELECT * FROM exam ORDER BY `id` DESC LIMIT 0,2;
      \n
    • \n
    \n
  • \n
\n

子查询与嵌套查询

where 表达式【这个值是计算出来的】

\n

即:where 后面还跟select语句

\n

MySQL常用函数

数学运算

1
2
3
4
5
6
7
8
abs()

ceiling() -- 向上取整 9.1 -> 10
floor() -- 向下取整 9.1 -> 9

rand() -- 返回0~1随机数

sign() -- 判断一个数符号 0->0 负数->-1 正数->1
\n

字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
char_length()  -- 字符串长度

concat('a', 'b', 'c') -- 字符串连接 返回abc

-- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2
insert(str1, n1, len, str2)
-- 如
select insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef

-- 将一个字符串str1中的指定子字符串substr替换为新字符串str2
replace(str1, substr, str2)

-- 截取指定字符串
substr(str, a, len); -- 截取str中第a个开始,共len个字符的子串

-- 大小写转换
upper()
lower()

-- 返回在str第一次出现的字符串substr的索引
instr(str, substr) -- 没有则返回0

-- 反转字符串
reverse(str)

\n

时间-日期函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 当前日期
SELECT CURRENT_DATE()
SELECT CURDATE()

SELECT NOW(); -- 当前时间 2021-04-20 20:05:44
SELECT LOCALTIME(); -- 本地时间
SELECT SYSDATE(); -- 系统时间

SELECT YEAR(NOW()); -- 年
SELECT MONTH(NOW()); -- 月
SELECT DAY(NOW());\t-- 日
SELECT HOUR(NOW());\t-- 时
SELECT MINUTE(NOW()); -- 分
SELECT SECOND(NOW()); -- 秒
\n

系统函数

1
2
3
SELECT SYSTEM_USER();  -- 当前系统用户
SELECT USER()\t\t\t-- 当前用户
SELECT VERSION()\t\t-- 版本号
\n

聚合函数(常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
count() -- 计数有多少个记录
\tselect count(字段) from tablename; -- 忽略所有null值
\tselect count(*) from tablename; -- 不会忽略null值,计算行数
\tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同
\t
sum()
\tselect sum(字段) from tablename;
\t
avg()
\tselect avg(字段) from tablename;

max()
\tselect max(字段) from tablename;

min()
\tselect min(字段) from tablename;
\n
\n

select语法总结

\n

\"image-20210420202906345\"

\n
\n

数据库级别的md5摘要算法

MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】

\n

md5不可逆,具体值的md5值是一样的

\n

md5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样

\n

SQL使用函数 MD5()进行加密

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

MySQL数据管理

关系操作符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符含义例子结果
=等于5=6false
<> 或 !=不等于5<>6true
>
<
<=
>=
!<不小于
!>不大于
\n
\n

主键

主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。

\n

在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,students表的两行记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idclass_idnamegenderscore
11小明M90
21小红F95
\n
\n

每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。

\n

假设我们把name字段作为主键,那么通过名字小明小红就能唯一确定一条记录。但是,这么设定,就没法存储同名的同学了,因为插入相同主键的两条记录是不被允许的。

\n

对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。

\n

联合主键

关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。

\n

对于联合主键,允许一列有重复,只要不是所有主键列都重复即可

\n

外键(了解)

当我们用主键唯一标识记录时,我们就可以在students表中确定任意一个学生的记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idnameother columns…
1小明
2小红
\n
\n

我们还可以在classes表中确定任意一个班级记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idnameother columns…
1一班
2二班
\n
\n

但是我们如何确定students表的一条记录,例如,id=1的小明,属于哪个班级呢?

\n

由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个classes的记录可以对应多个students表的记录。

\n

为了表达这种一对多的关系,我们需要在students表中加入一列class_id,让它的值与classes表的某条记录相对应:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idclass_idnameother columns…
11小明
21小红
52小白
\n
\n

这样,我们就可以根据class_id这个列直接定位出一个students表的记录应该对应到classes的哪条记录。

\n

给一张表 students 添加外键约束:

\n
1
2
3
4
ALTER TABLE students
ADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意
FOREIGN KEY (class_id) # 指定了`class_id`作为外键
REFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)
\n

添加了外键的表(students)为从表,被外键引用的表(classes)为主表,要删除主表前需先删除从表

\n
\n

注意:以上外键是数据库级别外键,不推荐使用

\n

最佳实现:

\n
    \n
  • 数据库就是单纯的表,只有行(记录)、列(字段)
  • \n
  • 想使用多张表数据,想用外键(用程序如Java语言实现)
  • \n
\n
\n

DML语言(全部记住)

即数据操作语言

\n
    \n
  • 插入(增)一行(记录):insert into…values…
  • \n
\n
1
2
3
4
5
6
insert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...;

例:

INSERT INTO `student` ( `id`, `name` )
VALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');
\n

\"\"

\n
    \n
  • 删除一列(字段):
      \n
    • delete
    • \n
    • truncate
    • \n
    • 相同:都会删除记录,表的结构和索引、约束不会变
    • \n
    • 区别:
        \n
      1. truncate 重新设置自增列,计数器会归零;delete不会影响自增
      2. \n
      3. truncate 不会影响事务
      4. \n
      \n
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
-- 删除一条记录
delete from `student` where id = '1001';

-- 删除所有记录(不建议这样写)
delete from `student`;

\n
1
2
-- 清空表的所有记录,但表的结构和索引不变
truncate table `student`;
\n
\n

对不同引擎的数据库使用delete语句后,重启数据库,现象:(了解)

\n
    \n
  • InnoDB:自增列会从1开始(因为保存在内存中)
  • \n
  • MyISAM:继续从上一个自增量开始(保存在文件中)
  • \n
\n
\n
    \n
  • 修改:update…set…
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
-- 用where指定条件
update `student` set `name`='wangwu' where `id` = `1001`;

-- 不指定条件情况下,会改动所有行(即记录)
update `student` set `name`='wangwu';

-- 修改多个字段, 用逗号隔开
update `student` set `name`='wangwu', `age`=20 where `id` = `1001`;

-- 通过多个条件定位数据, 用逻辑运算符and、or...
update `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;
\n
\n

注意 【=】 含义:

\n

在set这样的语句后面表示赋值;

\n

在where后面表示关系运算符,表示比较;

\n
\n

DQL查询数据(最重要)

DQL(data query language:数据查询语言)

\n
    \n
  • 所有查询(简单、复杂)都用它
  • \n
  • 数据库中最核心、重要的语句
  • \n
  • 使用频率最高的语句
  • \n
\n

基本查询操作 select

    \n
  1. 查询字段

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    -- 查询全部字段  select 字段 from 表
    select * from `student`;

    -- 查询指定字段 select 字段1[, 字段2, ...] from 表;
    select name, age from student;

    -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写
    select name as `学生姓名`, age as `年龄` from `student` as `s`;

    -- 函数concat(a, b): 连接a、b字符串
    select concat('姓名:', name) as 新名字 from student;
    \n
  2. \n
\n
    \n
  1. 去重 distinct

    \n
    1
    2
    -- 去除select后重复的数据,只留下一条
    select distinct `age` from student;
    \n
  2. \n
  3. 其他

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -- 查询系统版本
    select version(); -- version()是个函数

    -- 计算
    select 100*3-1 as 计算结果;

    -- 所有学员考试成绩 +1 分
    select `name`, `grade` + 1 as 提成1分后 from `student`;

    -- 查询系统内置变量值
    select @@auto_increment_increment; -- 查询自增步长

    \n
  4. \n
\n

数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。

\n
1
select 表达式 from 表;
\n

where语句

检索数据中符合条件的值

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
逻辑运算符语法描述
and &&a and b、 a&&b2个都为真返回真
or ``a or b 、 `ab`。。。
not !not a、 !a。。。
\n
\n
1
2
3
4
5
6
7
8
SELECT
\tid AS 学号,
\t`name` AS 姓名
FROM
\tstudent
WHERE
\tgrade > 80
\tAND age < 21;
\n
1
2
3
4
5
6
7
8
SELECT
\t`id` AS 学号,
\t`name` AS 姓名,
\t`grade` as 成绩
FROM
\tstudent
WHERE
\tgrade BETWEEN 70 AND 90;
\n

模糊查询

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符语法描述
is nulla is null如果a是null,则为真
is not nulla is not null如果a不是null,则为真
betweena between b and c若a在b和c之间,结果为真
likea like bsql匹配,若a匹配b,结果为真
ina in(a1,a2,a3,a4,…)若a是a1,a2,a3,a4…中的一个值,结果为真
\n
\n

like:通常结合 %_ 来使用

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 查找以王字开头的name  
-- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符
SELECT `id`, `name` FROM student
WHERE `name` LIKE '王%'; -- 表示以【王】字开头


-- 查找以王字开头且【名字为2个字】的name
SELECT `id`, `name` FROM student
WHERE `name` LIKE '王__'; -- 两个下划线


-- 查找生日不是null的
SELECT `id`, `name` FROM student
WHERE `birthday` is not null;
\n

联表查询 join on

同时在2个表中查找数据(必须要有交集)

\n

\"img\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
有2张表(都有id、name字段):
1. `student`:班级学生基本信息
2. `exam`:年级学生考试信息

【问题:查找出本班级学生的考试信息】
*/

-- =====================================
-- 取交集 inner join
select s.id as 学号, s.name as 姓名
from student as s inner join exam as e -- as可以省略
on s.id = e.id;

-- =====================================
-- 仅左边的表有,left join
SELECT
\ts.id 学号, -- 省略as
\ts.NAME 姓名
FROM
\tstudent s
\tLEFT JOIN exam e ON s.NAME = e.NAME
WHERE
\te.`name` IS NULL;
-- =====================================

-- 两个表的所有人

\n
\n

mysql 不支持 full outer join

\n
\n

自连接及联表查询

自己的表和自己的表连接,核心:1张表拆为2张一样的表

\n

分页和排序

    \n
  • 排序 order by

    \n
      \n
    • 升序用 ASC, 降序用DESC

      \n
    • \n
    • order by 表示查询结果怎么排(升序、降序),通过哪个字段排

      \n

      比如:

      \n
      1
      2
      3
      -- order 字段  ASC 升序
      -- order 字段 DESC 降序
      SELECT * FROM exam ORDER BY `grade` ASC; -- 成绩按升序排
      \n
    • \n
    \n
  • \n
  • 分页 limit:可以缓解数据库压力、给人体验更好、瀑布流

    \n
      \n
    • 语法:

      \n
      1
      2
      3
      4
      -- limit 起始下标(从0开始) 页面大小

      -- 从第0条数据开始,显示2条数据
      SELECT * FROM exam ORDER BY `id` DESC LIMIT 0,2;
      \n
    • \n
    \n
  • \n
\n

子查询与嵌套查询

where 表达式【这个值是计算出来的】

\n

即:where 后面还跟select语句

\n

MySQL常用函数

数学运算

1
2
3
4
5
6
7
8
abs()

ceiling() -- 向上取整 9.1 -> 10
floor() -- 向下取整 9.1 -> 9

rand() -- 返回0~1随机数

sign() -- 判断一个数符号 0->0 负数->-1 正数->1
\n

字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
char_length()  -- 字符串长度

concat('a', 'b', 'c') -- 字符串连接 返回abc

-- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2
insert(str1, n1, len, str2)
-- 如
select insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef

-- 将一个字符串str1中的指定子字符串substr替换为新字符串str2
replace(str1, substr, str2)

-- 截取指定字符串
substr(str, a, len); -- 截取str中第a个开始,共len个字符的子串

-- 大小写转换
upper()
lower()

-- 返回在str第一次出现的字符串substr的索引
instr(str, substr) -- 没有则返回0

-- 反转字符串
reverse(str)

\n

时间-日期函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 当前日期
SELECT CURRENT_DATE()
SELECT CURDATE()

SELECT NOW(); -- 当前时间 2021-04-20 20:05:44
SELECT LOCALTIME(); -- 本地时间
SELECT SYSDATE(); -- 系统时间

SELECT YEAR(NOW()); -- 年
SELECT MONTH(NOW()); -- 月
SELECT DAY(NOW());\t-- 日
SELECT HOUR(NOW());\t-- 时
SELECT MINUTE(NOW()); -- 分
SELECT SECOND(NOW()); -- 秒
\n

系统函数

1
2
3
SELECT SYSTEM_USER();  -- 当前系统用户
SELECT USER()\t\t\t-- 当前用户
SELECT VERSION()\t\t-- 版本号
\n

聚合函数(常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
count() -- 计数有多少个记录
\tselect count(字段) from tablename; -- 忽略所有null值
\tselect count(*) from tablename; -- 不会忽略null值,计算行数
\tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同
\t
sum()
\tselect sum(字段) from tablename;
\t
avg()
\tselect avg(字段) from tablename;

max()
\tselect max(字段) from tablename;

min()
\tselect min(字段) from tablename;
\n
\n

select语法总结

\n

\"image-20210420202906345\"

\n
\n

数据库级别的md5摘要算法

MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】

\n

md5不可逆,具体值的md5值是一样的

\n

md5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样

\n

SQL使用函数 MD5()进行加密

\n"},{"title":"MySql(5)-索引","abbrlink":"683d1a78","date":"2021-06-09T03:53:17.000Z","description":"MySQL数据库索引介绍与使用,索引可以提高查询效率","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# 索引\n\n**索引是**帮助MySQL高效获取数据的**数据结构**\n\n## 索引分类\n\n- 主键索引(primary key)\n\n - 唯一的标识,主键不可重复,只能有一个列为主键\n- 唯一索引(unique key)\n - 避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引\n- 常规索引(key/index)\n - 默认的,index、key关键字来设置\n- 全文索引(fulltext)\n\n\n\n\n\n## 索引使用\n\n1. 在创建表的时候给字段增加索引\n2. 创建完毕后,增加索引\n\n```mysql\n-- 显示所有索引信息\nshow index from tableA;\n\n-- 增加一个全文索引\nalter table tableA add fulltext index `studentName`;\n\n-- 分析sql执行状况 explain\nexplain select * from tableA;\n\n```\n\n\n\n\n\n## 索引使用原则\n\n- 不是越多越好\n- 不要对进程变动数据加索引\n- 小数据量的表不要添加索引\n- 索引一般加在常用来查询的字段\n\n\n\n## 索引数据结构\n\n- hash类型\n- B-tree类型(innoDB默认索引类型)\n\n\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/MySQL/5-索引.md","raw":"---\ntitle: MySql(5)-索引\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 683d1a78\ndate: 2021-06-09 11:53:17\ndescription: MySQL数据库索引介绍与使用,索引可以提高查询效率\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# 索引\n\n**索引是**帮助MySQL高效获取数据的**数据结构**\n\n## 索引分类\n\n- 主键索引(primary key)\n\n - 唯一的标识,主键不可重复,只能有一个列为主键\n- 唯一索引(unique key)\n - 避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引\n- 常规索引(key/index)\n - 默认的,index、key关键字来设置\n- 全文索引(fulltext)\n\n\n\n\n\n## 索引使用\n\n1. 在创建表的时候给字段增加索引\n2. 创建完毕后,增加索引\n\n```mysql\n-- 显示所有索引信息\nshow index from tableA;\n\n-- 增加一个全文索引\nalter table tableA add fulltext index `studentName`;\n\n-- 分析sql执行状况 explain\nexplain select * from tableA;\n\n```\n\n\n\n\n\n## 索引使用原则\n\n- 不是越多越好\n- 不要对进程变动数据加索引\n- 小数据量的表不要添加索引\n- 索引一般加在常用来查询的字段\n\n\n\n## 索引数据结构\n\n- hash类型\n- B-tree类型(innoDB默认索引类型)\n\n\n\n\n\n\n\n\n\n\n\n\n\n","slug":"MySQL/5-索引","published":1,"updated":"2021-09-08T13:05:57.536Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ou001qakve1mo0aal1","content":"

索引

索引是帮助MySQL高效获取数据的数据结构

\n

索引分类

    \n
  • 主键索引(primary key)

    \n
      \n
    • 唯一的标识,主键不可重复,只能有一个列为主键
    • \n
    \n
  • \n
  • 唯一索引(unique key)
      \n
    • 避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引
    • \n
    \n
  • \n
  • 常规索引(key/index)
      \n
    • 默认的,index、key关键字来设置
    • \n
    \n
  • \n
  • 全文索引(fulltext)
  • \n
\n

索引使用

    \n
  1. 在创建表的时候给字段增加索引
  2. \n
  3. 创建完毕后,增加索引
  4. \n
\n
1
2
3
4
5
6
7
8
9
-- 显示所有索引信息
show index from tableA;

-- 增加一个全文索引
alter table tableA add fulltext index `studentName`;

-- 分析sql执行状况 explain
explain select * from tableA;

\n

索引使用原则

    \n
  • 不是越多越好
  • \n
  • 不要对进程变动数据加索引
  • \n
  • 小数据量的表不要添加索引
  • \n
  • 索引一般加在常用来查询的字段
  • \n
\n

索引数据结构

    \n
  • hash类型
  • \n
  • B-tree类型(innoDB默认索引类型)
  • \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

索引

索引是帮助MySQL高效获取数据的数据结构

\n

索引分类

    \n
  • 主键索引(primary key)

    \n
      \n
    • 唯一的标识,主键不可重复,只能有一个列为主键
    • \n
    \n
  • \n
  • 唯一索引(unique key)
      \n
    • 避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引
    • \n
    \n
  • \n
  • 常规索引(key/index)
      \n
    • 默认的,index、key关键字来设置
    • \n
    \n
  • \n
  • 全文索引(fulltext)
  • \n
\n

索引使用

    \n
  1. 在创建表的时候给字段增加索引
  2. \n
  3. 创建完毕后,增加索引
  4. \n
\n
1
2
3
4
5
6
7
8
9
-- 显示所有索引信息
show index from tableA;

-- 增加一个全文索引
alter table tableA add fulltext index `studentName`;

-- 分析sql执行状况 explain
explain select * from tableA;

\n

索引使用原则

    \n
  • 不是越多越好
  • \n
  • 不要对进程变动数据加索引
  • \n
  • 小数据量的表不要添加索引
  • \n
  • 索引一般加在常用来查询的字段
  • \n
\n

索引数据结构

    \n
  • hash类型
  • \n
  • B-tree类型(innoDB默认索引类型)
  • \n
\n"},{"title":"MySql(6)-权限-备份","abbrlink":"d29e4d17","date":"2021-06-10T03:37:39.000Z","description":"关于数据库一些注意事项,如权限管理,备份管理,数据库设计、SQL注入问题","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n\n## 权限管理和备份\n\n\n\n```mysql\n-- 创建用户 create user 用户名 identified by '密码'\ncreate user 'kjn' identified by '123456';\n\n-- 修改当前用户密码\nset password = password('98765');\n\n-- 修改指定用户密码\nset password for '用户名' = password('98765');\n\n-- 重命名用户 rename ... to ...\nrename user 原来名字 to 新名字;\n\n-- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干\ngrant all privileges on *.* to 用户名;\n\n-- 查看权限\nshow grants for 用户名;\n\n\n-- 撤销权限\nrevoke all privileges on *.* from 用户名;\n\n\n```\n\n\n\n\n\n## 数据库备份\n\n备份方式\n\n- 直接拷贝物理文件\n\n- 在软件中导出\n\n navicat右键点击表导出\n\n- cmd命令行导出\n\n ```mysql\n -- mysqldump -h本机服务\n mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名\n ```\n\n- 导入(mysql命令行)\n\n ```mysql\n -- 导入D:/a.sql\n mysql > source d:/a.sql;\n ```\n\n \n\n## 规范数据库设计\n\n数据库比较复杂时就要设计\n\n**良好的数据库设计:**\n\n- 节省内存空间\n- 保证数据库完整性\n- 方便开发系统\n\n**软件开发中:**\n\n- 分析需求:分析业务和需要处理的数据库的需求\n- 概要设计:设计关系图E-R图\n\n\n\n**设计数据库步骤**(个人博客)\n\n- 收集信息,分析需求\n - 用户表(用户登录、注销、个人信息,写博客,创建分支)\n - 分类表(文章分类、谁创建的)\n - 文章表\n - 评论表\n - 粉丝表\n - 自定义表(系统信息、某个关键字、字段)\n - 说说表(用户id、内容、发表时间)\n- 标识实体(把需求落实到每个字段)\n- 标识实体之间关系\n - 写博客:user->blog\n - 创建分类:user->catagory\n - 关注:user->user\n - 粉丝链接:links\n - 评论:user->user-blog\n\n\n\n### 三大范式\n\n> 为什么需要数据规范化\n\n- 信息重写\n- 更新异常\n- 插入异常\n - 无法正常显示信息\n- 删除异常\n - 丢失有效信息\n\n\n\n#### 三大范式\n\n1. **第一范式(1NF)**\n\n 原子性:保持列不可再分\n\n2. **第二范式(2NF)**\n\n 前提:满足1NF\n\n 每张表只描述一件事\n\n3. **第三范式(3NF)**\n\n 前提:满足1NF与2NF\n\n 确保数据表中每一列数据都和主键直接相关,而不能间接相关\n\n\n\n#### 规范性和性能问题\n\n关联查询的表不能超过3张表\n\n- 考虑商业化的需求和目标(成本、用户体验),数据库性能更重要\n- 在规范性能的问题时候,需要适当考虑一下规范性\n- 可以故意给某些表增加一些冗余的字段(多表查询变为单表查询)\n- 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)\n\n\n\n\n\n## SQL注入问题\n\nsql存在漏洞,会被攻击,导致数据泄露\n\n\n\n\n\n\n\n## PrepareStatement对象\n\n防止SQL注入\n\n","source":"_posts/MySQL/6-权限-备份.md","raw":"---\ntitle: MySql(6)-权限-备份\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: d29e4d17\ndate: 2021-06-10 11:37:39\ndescription: 关于数据库一些注意事项,如权限管理,备份管理,数据库设计、SQL注入问题\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n\n## 权限管理和备份\n\n\n\n```mysql\n-- 创建用户 create user 用户名 identified by '密码'\ncreate user 'kjn' identified by '123456';\n\n-- 修改当前用户密码\nset password = password('98765');\n\n-- 修改指定用户密码\nset password for '用户名' = password('98765');\n\n-- 重命名用户 rename ... to ...\nrename user 原来名字 to 新名字;\n\n-- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干\ngrant all privileges on *.* to 用户名;\n\n-- 查看权限\nshow grants for 用户名;\n\n\n-- 撤销权限\nrevoke all privileges on *.* from 用户名;\n\n\n```\n\n\n\n\n\n## 数据库备份\n\n备份方式\n\n- 直接拷贝物理文件\n\n- 在软件中导出\n\n navicat右键点击表导出\n\n- cmd命令行导出\n\n ```mysql\n -- mysqldump -h本机服务\n mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名\n ```\n\n- 导入(mysql命令行)\n\n ```mysql\n -- 导入D:/a.sql\n mysql > source d:/a.sql;\n ```\n\n \n\n## 规范数据库设计\n\n数据库比较复杂时就要设计\n\n**良好的数据库设计:**\n\n- 节省内存空间\n- 保证数据库完整性\n- 方便开发系统\n\n**软件开发中:**\n\n- 分析需求:分析业务和需要处理的数据库的需求\n- 概要设计:设计关系图E-R图\n\n\n\n**设计数据库步骤**(个人博客)\n\n- 收集信息,分析需求\n - 用户表(用户登录、注销、个人信息,写博客,创建分支)\n - 分类表(文章分类、谁创建的)\n - 文章表\n - 评论表\n - 粉丝表\n - 自定义表(系统信息、某个关键字、字段)\n - 说说表(用户id、内容、发表时间)\n- 标识实体(把需求落实到每个字段)\n- 标识实体之间关系\n - 写博客:user->blog\n - 创建分类:user->catagory\n - 关注:user->user\n - 粉丝链接:links\n - 评论:user->user-blog\n\n\n\n### 三大范式\n\n> 为什么需要数据规范化\n\n- 信息重写\n- 更新异常\n- 插入异常\n - 无法正常显示信息\n- 删除异常\n - 丢失有效信息\n\n\n\n#### 三大范式\n\n1. **第一范式(1NF)**\n\n 原子性:保持列不可再分\n\n2. **第二范式(2NF)**\n\n 前提:满足1NF\n\n 每张表只描述一件事\n\n3. **第三范式(3NF)**\n\n 前提:满足1NF与2NF\n\n 确保数据表中每一列数据都和主键直接相关,而不能间接相关\n\n\n\n#### 规范性和性能问题\n\n关联查询的表不能超过3张表\n\n- 考虑商业化的需求和目标(成本、用户体验),数据库性能更重要\n- 在规范性能的问题时候,需要适当考虑一下规范性\n- 可以故意给某些表增加一些冗余的字段(多表查询变为单表查询)\n- 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)\n\n\n\n\n\n## SQL注入问题\n\nsql存在漏洞,会被攻击,导致数据泄露\n\n\n\n\n\n\n\n## PrepareStatement对象\n\n防止SQL注入\n\n","slug":"MySQL/6-权限-备份","published":1,"updated":"2021-09-08T13:05:51.506Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ow001uakve6hqegt4b","content":"

权限管理和备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- 创建用户 create user 用户名 identified by '密码'
create user 'kjn' identified by '123456';

-- 修改当前用户密码
set password = password('98765');

-- 修改指定用户密码
set password for '用户名' = password('98765');

-- 重命名用户 rename ... to ...
rename user 原来名字 to 新名字;

-- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干
grant all privileges on *.* to 用户名;

-- 查看权限
show grants for 用户名;


-- 撤销权限
revoke all privileges on *.* from 用户名;


\n

数据库备份

备份方式

\n
    \n
  • 直接拷贝物理文件

    \n
  • \n
  • 在软件中导出

    \n

    navicat右键点击表导出

    \n
  • \n
  • cmd命令行导出

    \n
    1
    2
    -- mysqldump -h本机服务
    mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名
    \n
  • \n
  • 导入(mysql命令行)

    \n
    1
    2
    -- 导入D:/a.sql
    mysql > source d:/a.sql;
    \n
  • \n
\n

规范数据库设计

数据库比较复杂时就要设计

\n

良好的数据库设计:

\n
    \n
  • 节省内存空间
  • \n
  • 保证数据库完整性
  • \n
  • 方便开发系统
  • \n
\n

软件开发中:

\n
    \n
  • 分析需求:分析业务和需要处理的数据库的需求
  • \n
  • 概要设计:设计关系图E-R图
  • \n
\n

设计数据库步骤(个人博客)

\n
    \n
  • 收集信息,分析需求
      \n
    • 用户表(用户登录、注销、个人信息,写博客,创建分支)
    • \n
    • 分类表(文章分类、谁创建的)
    • \n
    • 文章表
    • \n
    • 评论表
    • \n
    • 粉丝表
    • \n
    • 自定义表(系统信息、某个关键字、字段)
    • \n
    • 说说表(用户id、内容、发表时间)
    • \n
    \n
  • \n
  • 标识实体(把需求落实到每个字段)
  • \n
  • 标识实体之间关系
      \n
    • 写博客:user->blog
    • \n
    • 创建分类:user->catagory
    • \n
    • 关注:user->user
    • \n
    • 粉丝链接:links
    • \n
    • 评论:user->user-blog
    • \n
    \n
  • \n
\n

三大范式

\n

为什么需要数据规范化

\n
\n
    \n
  • 信息重写
  • \n
  • 更新异常
  • \n
  • 插入异常
      \n
    • 无法正常显示信息
    • \n
    \n
  • \n
  • 删除异常
      \n
    • 丢失有效信息
    • \n
    \n
  • \n
\n

三大范式

    \n
  1. 第一范式(1NF)

    \n

    原子性:保持列不可再分

    \n
  2. \n
  3. 第二范式(2NF)

    \n

    前提:满足1NF

    \n

    每张表只描述一件事

    \n
  4. \n
  5. 第三范式(3NF)

    \n

    前提:满足1NF与2NF

    \n

    确保数据表中每一列数据都和主键直接相关,而不能间接相关

    \n
  6. \n
\n

规范性和性能问题

关联查询的表不能超过3张表

\n
    \n
  • 考虑商业化的需求和目标(成本、用户体验),数据库性能更重要
  • \n
  • 在规范性能的问题时候,需要适当考虑一下规范性
  • \n
  • 可以故意给某些表增加一些冗余的字段(多表查询变为单表查询)
  • \n
  • 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)
  • \n
\n

SQL注入问题

sql存在漏洞,会被攻击,导致数据泄露

\n

PrepareStatement对象

防止SQL注入

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

权限管理和备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- 创建用户 create user 用户名 identified by '密码'
create user 'kjn' identified by '123456';

-- 修改当前用户密码
set password = password('98765');

-- 修改指定用户密码
set password for '用户名' = password('98765');

-- 重命名用户 rename ... to ...
rename user 原来名字 to 新名字;

-- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干
grant all privileges on *.* to 用户名;

-- 查看权限
show grants for 用户名;


-- 撤销权限
revoke all privileges on *.* from 用户名;


\n

数据库备份

备份方式

\n
    \n
  • 直接拷贝物理文件

    \n
  • \n
  • 在软件中导出

    \n

    navicat右键点击表导出

    \n
  • \n
  • cmd命令行导出

    \n
    1
    2
    -- mysqldump -h本机服务
    mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名
    \n
  • \n
  • 导入(mysql命令行)

    \n
    1
    2
    -- 导入D:/a.sql
    mysql > source d:/a.sql;
    \n
  • \n
\n

规范数据库设计

数据库比较复杂时就要设计

\n

良好的数据库设计:

\n
    \n
  • 节省内存空间
  • \n
  • 保证数据库完整性
  • \n
  • 方便开发系统
  • \n
\n

软件开发中:

\n
    \n
  • 分析需求:分析业务和需要处理的数据库的需求
  • \n
  • 概要设计:设计关系图E-R图
  • \n
\n

设计数据库步骤(个人博客)

\n
    \n
  • 收集信息,分析需求
      \n
    • 用户表(用户登录、注销、个人信息,写博客,创建分支)
    • \n
    • 分类表(文章分类、谁创建的)
    • \n
    • 文章表
    • \n
    • 评论表
    • \n
    • 粉丝表
    • \n
    • 自定义表(系统信息、某个关键字、字段)
    • \n
    • 说说表(用户id、内容、发表时间)
    • \n
    \n
  • \n
  • 标识实体(把需求落实到每个字段)
  • \n
  • 标识实体之间关系
      \n
    • 写博客:user->blog
    • \n
    • 创建分类:user->catagory
    • \n
    • 关注:user->user
    • \n
    • 粉丝链接:links
    • \n
    • 评论:user->user-blog
    • \n
    \n
  • \n
\n

三大范式

\n

为什么需要数据规范化

\n
\n
    \n
  • 信息重写
  • \n
  • 更新异常
  • \n
  • 插入异常
      \n
    • 无法正常显示信息
    • \n
    \n
  • \n
  • 删除异常
      \n
    • 丢失有效信息
    • \n
    \n
  • \n
\n

三大范式

    \n
  1. 第一范式(1NF)

    \n

    原子性:保持列不可再分

    \n
  2. \n
  3. 第二范式(2NF)

    \n

    前提:满足1NF

    \n

    每张表只描述一件事

    \n
  4. \n
  5. 第三范式(3NF)

    \n

    前提:满足1NF与2NF

    \n

    确保数据表中每一列数据都和主键直接相关,而不能间接相关

    \n
  6. \n
\n

规范性和性能问题

关联查询的表不能超过3张表

\n
    \n
  • 考虑商业化的需求和目标(成本、用户体验),数据库性能更重要
  • \n
  • 在规范性能的问题时候,需要适当考虑一下规范性
  • \n
  • 可以故意给某些表增加一些冗余的字段(多表查询变为单表查询)
  • \n
  • 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)
  • \n
\n

SQL注入问题

sql存在漏洞,会被攻击,导致数据泄露

\n

PrepareStatement对象

防止SQL注入

\n"},{"title":"MySql(7)-JDBC","abbrlink":"3f4cddeb","date":"2021-06-11T04:54:16.000Z","description":"Java中使用MySQL数据库,即JDBC","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# JDBC(重点)\n\n\n\n## idea连接MySQL数据库\n\n![image-20210812215126308](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-51-30_image-20210812215126308.png)\n\n上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:\n\n![image-20210812215624487](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-56-25_image-20210812215624487.png)\n\n\n\n\n\n## 数据库驱动\n\n驱动:声卡、显卡、数据库驱动\n\n\"image-20210421193728815\"\n\n\n\n程序通过数据库驱动操作数据库\n\n\n\n## JDBC\n\n**JDBC** (Java DataBase Connection) 是通过JAVA访问数据库;\n\nSUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 `JDBC`\n\n对于开发人员来说,我们只需要掌握 JDBC 接口即可\n\n\"image-20210421194311030\"\n\n\n\n\n\n**编写程序步骤:**\n\n1. 加载驱动\n2. 连接数据库(要有连接的服务器地址、用户名、密码) `DriverManager`\n3. 获取SQL对象\n4. 执行SQL语句,获取返回结果\n5. 释放连接\n\n```java\npackage com.jdbcTest;\n\nimport java.sql.*;\n\npublic class FirstJdbcDemo {\n public static void main(String[] args) throws ClassNotFoundException,SQLException {\n // 1.加载驱动\n Class.forName(\"com.mysql.cj.jdbc.Driver\"); //固定写法,mysql5版本没有【cj】\n\n // 2.链接信息url\n String url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true\";\n String username = \"root\";\n String password = \"admin\";\n\n // 3. 连接数据库\n Connection connection = DriverManager.getConnection(url, username, password);\n\n // 获取SQL对象\n Statement statement = connection.createStatement();\n\n // 执行SQL语句,获取返回的数据\n String sql = \"SELECT * FROM `student`\";\n ResultSet resultSet = statement.executeQuery(sql); //执行\n\n while(resultSet.next()){\n System.out.println(\"id: \" + resultSet.getObject(\"id\"));\n System.out.println(\"name: \" + resultSet.getObject(\"name\"));\n System.out.println(\"email: \" + resultSet.getObject(\"email\"));\n System.out.println(\"=============================\");\n }\n\n //6. 释放连接\n resultSet.close();\n statement.close();\n connection.close();\n\n }\n\n}\n\n```\n\n\n\n> url:\n>\n> ```java\n> url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true\";\n> ```\n\n\n\n> connection 代表数据库\n\n\n\n> Statement 执行SQL语句的对象\n\n\n\n> ResultSet 获得查询结果集,有以下方法\n>\n> - next():移动到下一个记录\n> - getObject(): 获取结果(不知道字段类型时)\n> - getString(): 获取字符串字段(知道字段类型)\n\n\n\n\n\n","source":"_posts/MySQL/7-JDBC.md","raw":"---\ntitle: MySql(7)-JDBC\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 3f4cddeb\ndate: 2021-06-11 12:54:16\ndescription: Java中使用MySQL数据库,即JDBC\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# JDBC(重点)\n\n\n\n## idea连接MySQL数据库\n\n![image-20210812215126308](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-51-30_image-20210812215126308.png)\n\n上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:\n\n![image-20210812215624487](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-56-25_image-20210812215624487.png)\n\n\n\n\n\n## 数据库驱动\n\n驱动:声卡、显卡、数据库驱动\n\n\"image-20210421193728815\"\n\n\n\n程序通过数据库驱动操作数据库\n\n\n\n## JDBC\n\n**JDBC** (Java DataBase Connection) 是通过JAVA访问数据库;\n\nSUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 `JDBC`\n\n对于开发人员来说,我们只需要掌握 JDBC 接口即可\n\n\"image-20210421194311030\"\n\n\n\n\n\n**编写程序步骤:**\n\n1. 加载驱动\n2. 连接数据库(要有连接的服务器地址、用户名、密码) `DriverManager`\n3. 获取SQL对象\n4. 执行SQL语句,获取返回结果\n5. 释放连接\n\n```java\npackage com.jdbcTest;\n\nimport java.sql.*;\n\npublic class FirstJdbcDemo {\n public static void main(String[] args) throws ClassNotFoundException,SQLException {\n // 1.加载驱动\n Class.forName(\"com.mysql.cj.jdbc.Driver\"); //固定写法,mysql5版本没有【cj】\n\n // 2.链接信息url\n String url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true\";\n String username = \"root\";\n String password = \"admin\";\n\n // 3. 连接数据库\n Connection connection = DriverManager.getConnection(url, username, password);\n\n // 获取SQL对象\n Statement statement = connection.createStatement();\n\n // 执行SQL语句,获取返回的数据\n String sql = \"SELECT * FROM `student`\";\n ResultSet resultSet = statement.executeQuery(sql); //执行\n\n while(resultSet.next()){\n System.out.println(\"id: \" + resultSet.getObject(\"id\"));\n System.out.println(\"name: \" + resultSet.getObject(\"name\"));\n System.out.println(\"email: \" + resultSet.getObject(\"email\"));\n System.out.println(\"=============================\");\n }\n\n //6. 释放连接\n resultSet.close();\n statement.close();\n connection.close();\n\n }\n\n}\n\n```\n\n\n\n> url:\n>\n> ```java\n> url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true\";\n> ```\n\n\n\n> connection 代表数据库\n\n\n\n> Statement 执行SQL语句的对象\n\n\n\n> ResultSet 获得查询结果集,有以下方法\n>\n> - next():移动到下一个记录\n> - getObject(): 获取结果(不知道字段类型时)\n> - getString(): 获取字符串字段(知道字段类型)\n\n\n\n\n\n","slug":"MySQL/7-JDBC","published":1,"updated":"2021-09-10T03:03:10.248Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ox001xakvegmtv0lsp","content":"

JDBC(重点)

idea连接MySQL数据库

\"image-20210812215126308\"

\n

上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:

\n

\"image-20210812215624487\"

\n

数据库驱动

驱动:声卡、显卡、数据库驱动

\n

\"image-20210421193728815\"

\n

程序通过数据库驱动操作数据库

\n

JDBC

JDBC (Java DataBase Connection) 是通过JAVA访问数据库;

\n

SUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 JDBC

\n

对于开发人员来说,我们只需要掌握 JDBC 接口即可

\n

\"image-20210421194311030\"

\n

编写程序步骤:

\n
    \n
  1. 加载驱动
  2. \n
  3. 连接数据库(要有连接的服务器地址、用户名、密码) DriverManager
  4. \n
  5. 获取SQL对象
  6. \n
  7. 执行SQL语句,获取返回结果
  8. \n
  9. 释放连接
  10. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.jdbcTest;

import java.sql.*;

public class FirstJdbcDemo {
public static void main(String[] args) throws ClassNotFoundException,SQLException {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver"); //固定写法,mysql5版本没有【cj】

// 2.链接信息url
String url = "jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true";
String username = "root";
String password = "admin";

// 3. 连接数据库
Connection connection = DriverManager.getConnection(url, username, password);

// 获取SQL对象
Statement statement = connection.createStatement();

// 执行SQL语句,获取返回的数据
String sql = "SELECT * FROM `student`";
ResultSet resultSet = statement.executeQuery(sql); //执行

while(resultSet.next()){
System.out.println("id: " + resultSet.getObject("id"));
System.out.println("name: " + resultSet.getObject("name"));
System.out.println("email: " + resultSet.getObject("email"));
System.out.println("=============================");
}

//6. 释放连接
resultSet.close();
statement.close();
connection.close();

}

}

\n
\n

url:

\n
1
url = "jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true";
\n

connection 代表数据库

\n

Statement 执行SQL语句的对象

\n

ResultSet 获得查询结果集,有以下方法

\n
    \n
  • next():移动到下一个记录
  • \n
  • getObject(): 获取结果(不知道字段类型时)
  • \n
  • getString(): 获取字符串字段(知道字段类型)
  • \n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

JDBC(重点)

idea连接MySQL数据库

\"image-20210812215126308\"

\n

上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:

\n

\"image-20210812215624487\"

\n

数据库驱动

驱动:声卡、显卡、数据库驱动

\n

\"image-20210421193728815\"

\n

程序通过数据库驱动操作数据库

\n

JDBC

JDBC (Java DataBase Connection) 是通过JAVA访问数据库;

\n

SUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 JDBC

\n

对于开发人员来说,我们只需要掌握 JDBC 接口即可

\n

\"image-20210421194311030\"

\n

编写程序步骤:

\n
    \n
  1. 加载驱动
  2. \n
  3. 连接数据库(要有连接的服务器地址、用户名、密码) DriverManager
  4. \n
  5. 获取SQL对象
  6. \n
  7. 执行SQL语句,获取返回结果
  8. \n
  9. 释放连接
  10. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.jdbcTest;

import java.sql.*;

public class FirstJdbcDemo {
public static void main(String[] args) throws ClassNotFoundException,SQLException {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver"); //固定写法,mysql5版本没有【cj】

// 2.链接信息url
String url = "jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true";
String username = "root";
String password = "admin";

// 3. 连接数据库
Connection connection = DriverManager.getConnection(url, username, password);

// 获取SQL对象
Statement statement = connection.createStatement();

// 执行SQL语句,获取返回的数据
String sql = "SELECT * FROM `student`";
ResultSet resultSet = statement.executeQuery(sql); //执行

while(resultSet.next()){
System.out.println("id: " + resultSet.getObject("id"));
System.out.println("name: " + resultSet.getObject("name"));
System.out.println("email: " + resultSet.getObject("email"));
System.out.println("=============================");
}

//6. 释放连接
resultSet.close();
statement.close();
connection.close();

}

}

\n
\n

url:

\n
1
url = "jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true";
\n

connection 代表数据库

\n

Statement 执行SQL语句的对象

\n

ResultSet 获得查询结果集,有以下方法

\n
    \n
  • next():移动到下一个记录
  • \n
  • getObject(): 获取结果(不知道字段类型时)
  • \n
  • getString(): 获取字符串字段(知道字段类型)
  • \n
\n
\n"},{"title":"Spring(1)-框架介绍","abbrlink":"3afd7950","date":"2021-07-23T04:11:32.000Z","description":"Java的spring框架介绍","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# spring框架介绍\n\n\n\n## spring是什么\n\n1. spring框架是一个开源JavaEE应用程序\n\n2. 主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西\n3. spring是基于分布式的应用程序\n - 基于轻量级框架\n - 配置管理\n - Bean对象实例化-IOC\n - 集成第三方的框架\n - mybatis\n - springMVC\n - ……\n - 自带服务\n - 邮件mail发送\n - 定时任务\n - 消息处理(异步处理)\n\n\n\n## spring结构\n\n1. Dao层:\n - jdbc操作\n - 对应框架:mybatis\n2. Service层\n - 没有对应框架\n3. controller层\n - servlet(接收请求 响应数据 地址匹配 页面转发)\n - 对应框架:springMVC\n\n\n\n## spring优点\n\n- 控制反转(IOC),面向切面编程(AOP)\n- 轻量,非入侵\n- 支持事务\n\n一句话:spring就是一个轻量级的**控制反转**和**面向切面编程**的框架\n\n\n\n\n\n## spring模块划分\n\n- SpringIOC\n- springAOP\n- springJDBC+事务\n- springWeb\n\n\n\n","source":"_posts/Spring/1-spring框架介绍.md","raw":"---\ntitle: Spring(1)-框架介绍\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 3afd7950\ndate: 2021-07-23 12:11:32\ndescription: Java的spring框架介绍\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# spring框架介绍\n\n\n\n## spring是什么\n\n1. spring框架是一个开源JavaEE应用程序\n\n2. 主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西\n3. spring是基于分布式的应用程序\n - 基于轻量级框架\n - 配置管理\n - Bean对象实例化-IOC\n - 集成第三方的框架\n - mybatis\n - springMVC\n - ……\n - 自带服务\n - 邮件mail发送\n - 定时任务\n - 消息处理(异步处理)\n\n\n\n## spring结构\n\n1. Dao层:\n - jdbc操作\n - 对应框架:mybatis\n2. Service层\n - 没有对应框架\n3. controller层\n - servlet(接收请求 响应数据 地址匹配 页面转发)\n - 对应框架:springMVC\n\n\n\n## spring优点\n\n- 控制反转(IOC),面向切面编程(AOP)\n- 轻量,非入侵\n- 支持事务\n\n一句话:spring就是一个轻量级的**控制反转**和**面向切面编程**的框架\n\n\n\n\n\n## spring模块划分\n\n- SpringIOC\n- springAOP\n- springJDBC+事务\n- springWeb\n\n\n\n","slug":"Spring/1-spring框架介绍","published":1,"updated":"2021-09-10T03:17:21.676Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oy0020akvegdxh7rqn","content":"

spring框架介绍

spring是什么

    \n
  1. spring框架是一个开源JavaEE应用程序

    \n
  2. \n
  3. 主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西

    \n
  4. \n
  5. spring是基于分布式的应用程序
      \n
    • 基于轻量级框架
        \n
      • 配置管理
      • \n
      • Bean对象实例化-IOC
      • \n
      \n
    • \n
    • 集成第三方的框架
        \n
      • mybatis
      • \n
      • springMVC
      • \n
      • ……
      • \n
      \n
    • \n
    • 自带服务
        \n
      • 邮件mail发送
      • \n
      • 定时任务
      • \n
      • 消息处理(异步处理)
      • \n
      \n
    • \n
    \n
  6. \n
\n

spring结构

    \n
  1. Dao层:
      \n
    • jdbc操作
    • \n
    • 对应框架:mybatis
    • \n
    \n
  2. \n
  3. Service层
      \n
    • 没有对应框架
    • \n
    \n
  4. \n
  5. controller层
      \n
    • servlet(接收请求 响应数据 地址匹配 页面转发)
    • \n
    • 对应框架:springMVC
    • \n
    \n
  6. \n
\n

spring优点

    \n
  • 控制反转(IOC),面向切面编程(AOP)
  • \n
  • 轻量,非入侵
  • \n
  • 支持事务
  • \n
\n

一句话:spring就是一个轻量级的控制反转面向切面编程的框架

\n

spring模块划分

    \n
  • SpringIOC
  • \n
  • springAOP
  • \n
  • springJDBC+事务
  • \n
  • springWeb
  • \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

spring框架介绍

spring是什么

    \n
  1. spring框架是一个开源JavaEE应用程序

    \n
  2. \n
  3. 主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西

    \n
  4. \n
  5. spring是基于分布式的应用程序
      \n
    • 基于轻量级框架
        \n
      • 配置管理
      • \n
      • Bean对象实例化-IOC
      • \n
      \n
    • \n
    • 集成第三方的框架
        \n
      • mybatis
      • \n
      • springMVC
      • \n
      • ……
      • \n
      \n
    • \n
    • 自带服务
        \n
      • 邮件mail发送
      • \n
      • 定时任务
      • \n
      • 消息处理(异步处理)
      • \n
      \n
    • \n
    \n
  6. \n
\n

spring结构

    \n
  1. Dao层:
      \n
    • jdbc操作
    • \n
    • 对应框架:mybatis
    • \n
    \n
  2. \n
  3. Service层
      \n
    • 没有对应框架
    • \n
    \n
  4. \n
  5. controller层
      \n
    • servlet(接收请求 响应数据 地址匹配 页面转发)
    • \n
    • 对应框架:springMVC
    • \n
    \n
  6. \n
\n

spring优点

    \n
  • 控制反转(IOC),面向切面编程(AOP)
  • \n
  • 轻量,非入侵
  • \n
  • 支持事务
  • \n
\n

一句话:spring就是一个轻量级的控制反转面向切面编程的框架

\n

spring模块划分

    \n
  • SpringIOC
  • \n
  • springAOP
  • \n
  • springJDBC+事务
  • \n
  • springWeb
  • \n
\n"},{"title":"Spring(10)-注解实现自动装配","abbrlink":"39f00355","date":"2021-07-28T12:52:40.000Z","description":"spring使用注解来完成自动装配","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n\n\n\n# 使用注解实现自动装配\n\n\n\n[点击跳转到工程](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master1)\n\n\n\nspring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入\n\n```java\n@Autowired //通过byType实现自动装配,而且必须要求这个对象存在\n@Resource\t\t//默认通过byName实现自动装配,如果找不到名字,就通过byType自动装配,2个都不行的话就报错\n```\n\n\n\n### 使用@Autowired注入\n\n要使用注解,首先要在xml文件中提供注解支持\n\n![image-20210801104937636](https://gitee.com/ajream/images/raw/master/img/2021-08-0110-49-42_image-20210801104937636.png)\n\n\n\nbeans.xml\n\n```xml\n\n\n\n \n\n \n \n \n\n\n\n```\n\n> 注意:使用byType自动装配时,若有多个相同类型的bean,把`primary`属性值设为true说明首先使用该bean\n\n\n\n在`Person`类中:(可以没有setter方法)\n\n```java\npublic class Person {\n @Autowired\n private Cat cat;\n//...\n}\n```\n\n\n\n@Autowired就相当于:\n\n```java\n\n```\n\n\n\n还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:\n\n```java\npublic class SimpleMovieLister {\n\n private Cat cat;\n\n @Autowired\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n // ...\n}\n```\n\n\n\n由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)\n\n```xml\n \n\n \n\n \n```\n\n> primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。\n\n### @Qualifier\n\n\n\n当有多个bean类型相同,但id不同时,可以使用`@Qualifier` 来指定使用哪一个bean\n\n```java\n\npublic class Person {\n @Autowired\n @Qualifier(value = \"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n\n\n### @Nullable\n\n字段使用了这个注解,说明该字段可以为 null\n\n### 使用@Resource 注入\n\n@Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。\n\n\n\n```java\n\npublic class Person {\n @Resource(name=\"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n","source":"_posts/Spring/10-注解实现自动装配.md","raw":"---\ntitle: Spring(10)-注解实现自动装配\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 39f00355\ndate: 2021-07-28 20:52:40\ndescription: spring使用注解来完成自动装配\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n\n\n\n# 使用注解实现自动装配\n\n\n\n[点击跳转到工程](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master1)\n\n\n\nspring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入\n\n```java\n@Autowired //通过byType实现自动装配,而且必须要求这个对象存在\n@Resource\t\t//默认通过byName实现自动装配,如果找不到名字,就通过byType自动装配,2个都不行的话就报错\n```\n\n\n\n### 使用@Autowired注入\n\n要使用注解,首先要在xml文件中提供注解支持\n\n![image-20210801104937636](https://gitee.com/ajream/images/raw/master/img/2021-08-0110-49-42_image-20210801104937636.png)\n\n\n\nbeans.xml\n\n```xml\n\n\n\n \n\n \n \n \n\n\n\n```\n\n> 注意:使用byType自动装配时,若有多个相同类型的bean,把`primary`属性值设为true说明首先使用该bean\n\n\n\n在`Person`类中:(可以没有setter方法)\n\n```java\npublic class Person {\n @Autowired\n private Cat cat;\n//...\n}\n```\n\n\n\n@Autowired就相当于:\n\n```java\n\n```\n\n\n\n还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:\n\n```java\npublic class SimpleMovieLister {\n\n private Cat cat;\n\n @Autowired\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n // ...\n}\n```\n\n\n\n由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)\n\n```xml\n \n\n \n\n \n```\n\n> primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。\n\n### @Qualifier\n\n\n\n当有多个bean类型相同,但id不同时,可以使用`@Qualifier` 来指定使用哪一个bean\n\n```java\n\npublic class Person {\n @Autowired\n @Qualifier(value = \"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n\n\n### @Nullable\n\n字段使用了这个注解,说明该字段可以为 null\n\n### 使用@Resource 注入\n\n@Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。\n\n\n\n```java\n\npublic class Person {\n @Resource(name=\"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n","slug":"Spring/10-注解实现自动装配","published":1,"updated":"2021-09-10T03:17:03.959Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p60024akvegkx6f24q","content":"

使用注解实现自动装配

点击跳转到工程

\n

spring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入

\n
1
2
@Autowired   //通过byType实现自动装配,而且必须要求这个对象存在
@Resource\t\t//默认通过byName实现自动装配,如果找不到名字,就通过byType自动装配,2个都不行的话就报错
\n

使用@Autowired注入

要使用注解,首先要在xml文件中提供注解支持

\n

\"image-20210801104937636\"

\n

beans.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

<bean id="dog" class="com.ajream.pojo.Dog"/>
<bean id="cat" class="com.ajream.pojo.Cat"/>
<bean id="person" class="com.ajream.pojo.Person" />


</beans>
\n
\n

注意:使用byType自动装配时,若有多个相同类型的bean,把primary属性值设为true说明首先使用该bean

\n
\n

Person类中:(可以没有setter方法)

\n
1
2
3
4
5
public class Person {
@Autowired
private Cat cat;
//...
}
\n

@Autowired就相当于:

\n
1
<bean class="com.ajream.pojo.Person"  autowire="byType"/>
\n

还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:

\n
1
2
3
4
5
6
7
8
9
10
11
public class SimpleMovieLister {

private Cat cat;

@Autowired
public void setCat(Cat cat) {
this.cat = cat;
}

// ...
}
\n

由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)

\n
1
2
3
4
5
<bean class="example.SimpleMovieCatalog" primary="true" />

<bean class="example.SimpleMovieCatalog"/>

<bean id="movieRecommender" class="example.MovieRecommender"/>
\n
\n

primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。

\n
\n

@Qualifier

当有多个bean类型相同,但id不同时,可以使用@Qualifier 来指定使用哪一个bean

\n
1
2
3
4
5
6
7

public class Person {
@Autowired
@Qualifier(value = "cat22")
private Cat cat;
//...
}
\n
1
2
3
4
<bean id="cat22" class="com.ajream.pojo.Cat"/>
<bean id="cat23" class="com.ajream.pojo.Cat"/>

<bean class="com.ajream.pojo.Person" autowire="byType"/>
\n

@Nullable

字段使用了这个注解,说明该字段可以为 null

\n

使用@Resource 注入

@Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。

\n
1
2
3
4
5
6

public class Person {
@Resource(name="cat22")
private Cat cat;
//...
}
\n
1
2
3
4
<bean id="cat22" class="com.ajream.pojo.Cat"/>
<bean id="cat23" class="com.ajream.pojo.Cat"/>

<bean class="com.ajream.pojo.Person" autowire="byType"/>
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

使用注解实现自动装配

点击跳转到工程

\n

spring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入

\n
1
2
@Autowired   //通过byType实现自动装配,而且必须要求这个对象存在
@Resource\t\t//默认通过byName实现自动装配,如果找不到名字,就通过byType自动装配,2个都不行的话就报错
\n

使用@Autowired注入

要使用注解,首先要在xml文件中提供注解支持

\n

\"image-20210801104937636\"

\n

beans.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

<bean id="dog" class="com.ajream.pojo.Dog"/>
<bean id="cat" class="com.ajream.pojo.Cat"/>
<bean id="person" class="com.ajream.pojo.Person" />


</beans>
\n
\n

注意:使用byType自动装配时,若有多个相同类型的bean,把primary属性值设为true说明首先使用该bean

\n
\n

Person类中:(可以没有setter方法)

\n
1
2
3
4
5
public class Person {
@Autowired
private Cat cat;
//...
}
\n

@Autowired就相当于:

\n
1
<bean class="com.ajream.pojo.Person"  autowire="byType"/>
\n

还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:

\n
1
2
3
4
5
6
7
8
9
10
11
public class SimpleMovieLister {

private Cat cat;

@Autowired
public void setCat(Cat cat) {
this.cat = cat;
}

// ...
}
\n

由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)

\n
1
2
3
4
5
<bean class="example.SimpleMovieCatalog" primary="true" />

<bean class="example.SimpleMovieCatalog"/>

<bean id="movieRecommender" class="example.MovieRecommender"/>
\n
\n

primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。

\n
\n

@Qualifier

当有多个bean类型相同,但id不同时,可以使用@Qualifier 来指定使用哪一个bean

\n
1
2
3
4
5
6
7

public class Person {
@Autowired
@Qualifier(value = "cat22")
private Cat cat;
//...
}
\n
1
2
3
4
<bean id="cat22" class="com.ajream.pojo.Cat"/>
<bean id="cat23" class="com.ajream.pojo.Cat"/>

<bean class="com.ajream.pojo.Person" autowire="byType"/>
\n

@Nullable

字段使用了这个注解,说明该字段可以为 null

\n

使用@Resource 注入

@Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。

\n
1
2
3
4
5
6

public class Person {
@Resource(name="cat22")
private Cat cat;
//...
}
\n
1
2
3
4
<bean id="cat22" class="com.ajream.pojo.Cat"/>
<bean id="cat23" class="com.ajream.pojo.Cat"/>

<bean class="com.ajream.pojo.Person" autowire="byType"/>
\n"},{"title":"Spring(12)-静态代理","abbrlink":"be39d4e8","date":"2021-07-29T11:42:38.000Z","description":"Java静态代理模式举例介绍","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 静态代理\n\n## 角色分析\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master)\n\n- 抽象角色:用接口或抽象类解决\n\n ```java\n package com.ajream.demo01;\n \n public interface Rent {\n public void rent();\n }\n \n ```\n\n \n\n- 真实角色:被代理角色,比如房东\n\n ```java\n package com.ajream.demo01;\n \n public class Host implements Rent{\n @Override\n public void rent() {\n System.out.println(\"房东出租了房子\");\n }\n }\n \n ```\n\n \n\n- 代理角色:代理真实角色,一般会有些附属操作\n\n ```java\n package com.ajream.demo01;\n \n public class Proxy implements Rent{\n \n Host host = new Host();\n \n public Proxy() {\n }\n \n public Proxy(Host host) {\n this.host = host;\n }\n \n // 中介帮房东租房子,但中介还会有一些附属操作\n public void rent() {\n host.rent();\n seeHouse();\n qianHeTong();\n }\n \n //中介的附属操作\n public void seeHouse(){\n System.out.println(\"中介带你看房子。。。\");\n }\n \n public void qianHeTong(){\n System.out.println(\"签合同。。。\");\n }\n }\n \n ```\n\n \n\n- 客户:访问代理对象的人\n\n ```java\n package com.ajream.demo01;\n \n import org.junit.Test;\n \n public class Client {\n \n @Test\n public void test(){\n // 找房东租房子\n Host host = new Host();\n \n \n // 客户要租入房子,所以把房东丢给中介,让中介帮忙\n Proxy p = new Proxy(host);\n p.rent();\n }\n \n }\n \n ```\n\n \n\n\n\n> 代理模式好处:\n>\n> 1. 角色操作更加纯粹,不用取关注一些公共业务\n> 2. 公共业务交给代理角色,实现业务分工\n> 3. 公共业务发生扩展时,方便集中管理\n>\n> 缺点:\n>\n> 1. 一个真实角色会产生一个代理角色,代码量翻倍\n\n\n\n## 代理模式实现业务\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master2)\n\n\n\n用代理模式为业务添加日志打印功能\n\n\n\n业务接口\n\n```java\npackage com.ajream.demo02;\n\n//业务接口\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n\n```\n\n\n\n业务实现类(实现增删改查功能)\n\n```java\npackage com.ajream.demo02;\n\n//业务实现\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n\n\n【重点】业务代理类(添加日志打印功能)\n\n```java\npackage com.ajream.demo02;\n\npublic class UserServiceProxy implements UserService{\n\n UserServiceImpl userService;\n\n public UserServiceProxy() {\n }\n\n public void setUserService(UserServiceImpl userService) {\n this.userService = userService;\n }\n\n @Override\n public void add() {\n log(\"add\");\n userService.add();\n }\n\n @Override\n public void delete() {\n log(\"delete\");\n userService.delete();\n }\n\n @Override\n public void update() {\n log(\"update\");\n userService.update();\n }\n\n @Override\n public void query() {\n log(\"query\");\n userService.query();\n }\n\n //代理的额外操作,打印日志\n public void log(String msg){\n System.out.println(\"使用了\" + msg + \"方法\" );\n }\n}\n\n```\n\n\n\n客户\n\n```java\npackage com.ajream.demo02;\n\nimport org.junit.Test;\n\npublic class Client {\n @Test\n public void test1(){\n UserServiceImpl userService = new UserServiceImpl();\n\n UserServiceProxy proxy = new UserServiceProxy();\n proxy.setUserService(userService);\n\n proxy.add();\n }\n}\n\n```\n\n","source":"_posts/Spring/12-静态代理.md","raw":"---\ntitle: Spring(12)-静态代理\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: be39d4e8\ndate: 2021-07-29 19:42:38\ndescription: Java静态代理模式举例介绍\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 静态代理\n\n## 角色分析\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master)\n\n- 抽象角色:用接口或抽象类解决\n\n ```java\n package com.ajream.demo01;\n \n public interface Rent {\n public void rent();\n }\n \n ```\n\n \n\n- 真实角色:被代理角色,比如房东\n\n ```java\n package com.ajream.demo01;\n \n public class Host implements Rent{\n @Override\n public void rent() {\n System.out.println(\"房东出租了房子\");\n }\n }\n \n ```\n\n \n\n- 代理角色:代理真实角色,一般会有些附属操作\n\n ```java\n package com.ajream.demo01;\n \n public class Proxy implements Rent{\n \n Host host = new Host();\n \n public Proxy() {\n }\n \n public Proxy(Host host) {\n this.host = host;\n }\n \n // 中介帮房东租房子,但中介还会有一些附属操作\n public void rent() {\n host.rent();\n seeHouse();\n qianHeTong();\n }\n \n //中介的附属操作\n public void seeHouse(){\n System.out.println(\"中介带你看房子。。。\");\n }\n \n public void qianHeTong(){\n System.out.println(\"签合同。。。\");\n }\n }\n \n ```\n\n \n\n- 客户:访问代理对象的人\n\n ```java\n package com.ajream.demo01;\n \n import org.junit.Test;\n \n public class Client {\n \n @Test\n public void test(){\n // 找房东租房子\n Host host = new Host();\n \n \n // 客户要租入房子,所以把房东丢给中介,让中介帮忙\n Proxy p = new Proxy(host);\n p.rent();\n }\n \n }\n \n ```\n\n \n\n\n\n> 代理模式好处:\n>\n> 1. 角色操作更加纯粹,不用取关注一些公共业务\n> 2. 公共业务交给代理角色,实现业务分工\n> 3. 公共业务发生扩展时,方便集中管理\n>\n> 缺点:\n>\n> 1. 一个真实角色会产生一个代理角色,代码量翻倍\n\n\n\n## 代理模式实现业务\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master2)\n\n\n\n用代理模式为业务添加日志打印功能\n\n\n\n业务接口\n\n```java\npackage com.ajream.demo02;\n\n//业务接口\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n\n```\n\n\n\n业务实现类(实现增删改查功能)\n\n```java\npackage com.ajream.demo02;\n\n//业务实现\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n\n\n【重点】业务代理类(添加日志打印功能)\n\n```java\npackage com.ajream.demo02;\n\npublic class UserServiceProxy implements UserService{\n\n UserServiceImpl userService;\n\n public UserServiceProxy() {\n }\n\n public void setUserService(UserServiceImpl userService) {\n this.userService = userService;\n }\n\n @Override\n public void add() {\n log(\"add\");\n userService.add();\n }\n\n @Override\n public void delete() {\n log(\"delete\");\n userService.delete();\n }\n\n @Override\n public void update() {\n log(\"update\");\n userService.update();\n }\n\n @Override\n public void query() {\n log(\"query\");\n userService.query();\n }\n\n //代理的额外操作,打印日志\n public void log(String msg){\n System.out.println(\"使用了\" + msg + \"方法\" );\n }\n}\n\n```\n\n\n\n客户\n\n```java\npackage com.ajream.demo02;\n\nimport org.junit.Test;\n\npublic class Client {\n @Test\n public void test1(){\n UserServiceImpl userService = new UserServiceImpl();\n\n UserServiceProxy proxy = new UserServiceProxy();\n proxy.setUserService(userService);\n\n proxy.add();\n }\n}\n\n```\n\n","slug":"Spring/12-静态代理","published":1,"updated":"2021-09-10T03:16:52.129Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p70028akve23cieqja","content":"

静态代理

角色分析

项目地址

\n
    \n
  • 抽象角色:用接口或抽象类解决

    \n
    1
    2
    3
    4
    5
    6
    package com.ajream.demo01;

    public interface Rent {
    public void rent();
    }

    \n
  • \n
\n
    \n
  • 真实角色:被代理角色,比如房东

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.ajream.demo01;

    public class Host implements Rent{
    @Override
    public void rent() {
    System.out.println("房东出租了房子");
    }
    }

    \n
  • \n
\n
    \n
  • 代理角色:代理真实角色,一般会有些附属操作

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package com.ajream.demo01;

    public class Proxy implements Rent{

    Host host = new Host();

    public Proxy() {
    }

    public Proxy(Host host) {
    this.host = host;
    }

    // 中介帮房东租房子,但中介还会有一些附属操作
    public void rent() {
    host.rent();
    seeHouse();
    qianHeTong();
    }

    //中介的附属操作
    public void seeHouse(){
    System.out.println("中介带你看房子。。。");
    }

    public void qianHeTong(){
    System.out.println("签合同。。。");
    }
    }

    \n
  • \n
\n
    \n
  • 客户:访问代理对象的人

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.ajream.demo01;

    import org.junit.Test;

    public class Client {

    @Test
    public void test(){
    // 找房东租房子
    Host host = new Host();


    // 客户要租入房子,所以把房东丢给中介,让中介帮忙
    Proxy p = new Proxy(host);
    p.rent();
    }

    }

    \n
  • \n
\n
\n

代理模式好处:

\n
    \n
  1. 角色操作更加纯粹,不用取关注一些公共业务
  2. \n
  3. 公共业务交给代理角色,实现业务分工
  4. \n
  5. 公共业务发生扩展时,方便集中管理
  6. \n
\n

缺点:

\n
    \n
  1. 一个真实角色会产生一个代理角色,代码量翻倍
  2. \n
\n
\n

代理模式实现业务

项目地址

\n

用代理模式为业务添加日志打印功能

\n

业务接口

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.demo02;

//业务接口
public interface UserService {
void add();
void delete();
void update();
void query();
}

\n

业务实现类(实现增删改查功能)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.ajream.demo02;

//业务实现
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加一个用户");
}

@Override
public void delete() {
System.out.println("删除一个用户");
}

@Override
public void update() {
System.out.println("修改一个用户");
}

@Override
public void query() {
System.out.println("查询一个用户");
}
}

\n

【重点】业务代理类(添加日志打印功能)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.ajream.demo02;

public class UserServiceProxy implements UserService{

UserServiceImpl userService;

public UserServiceProxy() {
}

public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}

@Override
public void add() {
log("add");
userService.add();
}

@Override
public void delete() {
log("delete");
userService.delete();
}

@Override
public void update() {
log("update");
userService.update();
}

@Override
public void query() {
log("query");
userService.query();
}

//代理的额外操作,打印日志
public void log(String msg){
System.out.println("使用了" + msg + "方法" );
}
}

\n

客户

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.demo02;

import org.junit.Test;

public class Client {
@Test
public void test1(){
UserServiceImpl userService = new UserServiceImpl();

UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);

proxy.add();
}
}

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

静态代理

角色分析

项目地址

\n
    \n
  • 抽象角色:用接口或抽象类解决

    \n
    1
    2
    3
    4
    5
    6
    package com.ajream.demo01;

    public interface Rent {
    public void rent();
    }

    \n
  • \n
\n
    \n
  • 真实角色:被代理角色,比如房东

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.ajream.demo01;

    public class Host implements Rent{
    @Override
    public void rent() {
    System.out.println("房东出租了房子");
    }
    }

    \n
  • \n
\n
    \n
  • 代理角色:代理真实角色,一般会有些附属操作

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package com.ajream.demo01;

    public class Proxy implements Rent{

    Host host = new Host();

    public Proxy() {
    }

    public Proxy(Host host) {
    this.host = host;
    }

    // 中介帮房东租房子,但中介还会有一些附属操作
    public void rent() {
    host.rent();
    seeHouse();
    qianHeTong();
    }

    //中介的附属操作
    public void seeHouse(){
    System.out.println("中介带你看房子。。。");
    }

    public void qianHeTong(){
    System.out.println("签合同。。。");
    }
    }

    \n
  • \n
\n
    \n
  • 客户:访问代理对象的人

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.ajream.demo01;

    import org.junit.Test;

    public class Client {

    @Test
    public void test(){
    // 找房东租房子
    Host host = new Host();


    // 客户要租入房子,所以把房东丢给中介,让中介帮忙
    Proxy p = new Proxy(host);
    p.rent();
    }

    }

    \n
  • \n
\n
\n

代理模式好处:

\n
    \n
  1. 角色操作更加纯粹,不用取关注一些公共业务
  2. \n
  3. 公共业务交给代理角色,实现业务分工
  4. \n
  5. 公共业务发生扩展时,方便集中管理
  6. \n
\n

缺点:

\n
    \n
  1. 一个真实角色会产生一个代理角色,代码量翻倍
  2. \n
\n
\n

代理模式实现业务

项目地址

\n

用代理模式为业务添加日志打印功能

\n

业务接口

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.demo02;

//业务接口
public interface UserService {
void add();
void delete();
void update();
void query();
}

\n

业务实现类(实现增删改查功能)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.ajream.demo02;

//业务实现
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加一个用户");
}

@Override
public void delete() {
System.out.println("删除一个用户");
}

@Override
public void update() {
System.out.println("修改一个用户");
}

@Override
public void query() {
System.out.println("查询一个用户");
}
}

\n

【重点】业务代理类(添加日志打印功能)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.ajream.demo02;

public class UserServiceProxy implements UserService{

UserServiceImpl userService;

public UserServiceProxy() {
}

public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}

@Override
public void add() {
log("add");
userService.add();
}

@Override
public void delete() {
log("delete");
userService.delete();
}

@Override
public void update() {
log("update");
userService.update();
}

@Override
public void query() {
log("query");
userService.query();
}

//代理的额外操作,打印日志
public void log(String msg){
System.out.println("使用了" + msg + "方法" );
}
}

\n

客户

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.demo02;

import org.junit.Test;

public class Client {
@Test
public void test1(){
UserServiceImpl userService = new UserServiceImpl();

UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);

proxy.add();
}
}

\n"},{"title":"Spring(11)-使用注解开发","abbrlink":"38b3e585","date":"2021-07-28T12:55:13.000Z","description":"spring使用注解开发,一种还要依赖xml配置文件,另一种可以完全抛弃xml配置文件,使用纯Java来完成属性注入","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 使用注解开发\n\n## 依然使用xml配置文件来管理\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master3/)\n\nspring4之后,要使用注解开发,必须保证aop的包导入了\n\n![image-20210801135559755](https://gitee.com/ajream/images/raw/master/img/2021-08-0113-56-04_image-20210801135559755.png)\n\n使用注解开发,要导入context约束,提供注解的支持\n\n```xml\n\n\n\n \n\n \n \n\n```\n\n\n\n1. bean\n\n ```java\n @component:\n //组件,在创建的类前面加上这个注解,说明这个类已经被spring管理了,相当于:\n //\n //没有参数时,只能以getBean(\"类名的小写字母\") 来获取bean\n @component(value=\"xxx\")\n //有参数时,可以用getBean(\"xxx\") 来获取bean\n ```\n\n Person类:\n\n ```java\n package com.ajream.pojo;\n \n import org.springframework.stereotype.Component;\n \n @Component\t\t\t\t//没有参数时,只能以getBean(\"person\") 来获取bean\n public class Person {\n public String name = \"张三\";\n }\n \n ```\n\n \n\n2. 属性注入\n\n\t ```java\n @Value(\"李四\"):\n //相当于\n ```\n\n \n\n ```java\n \n @Component\n public class Person {\n \n @Value(\"李四\")\t\t//注入属性值\n public String name;\n }\n ```\n\n 也可以在setter方法前使用:\n\n ```java\n @Component\n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n \n\n4. 衍生注解\n\n @Component有几个衍生注解,在web中一般按照mvc三层架构划分:\n\n - dao层:@Repository\n - service层:@Service\n - Controller层:@Controller\n\n 这4个注解功能一样的\n\n5. 作用域scope\n\n 单例\n\n ```java\n @Component\n @Scope(value = \"singleton\") \n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n 原型\n\n ```java\n @Scope(value = \"prototype\") \n ```\n\n6. 小结:\n\n xml与注解\n\n - xml:万能\n\n - 注解:不是自己的类用不了,维护相对复杂\n\n xml与注解最佳实践:\n\n - xml用来管理bean\n\n - 注解只负责属性注入\n\n - 注意:要让注解生效,必须开启注解的支持\n\n ```xml\n \n \n ```\n\n \n\n## 使用Java的方式配置spring\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master4/)\n\n在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置\n\n用MyConfig类来代替beans.xml:\n\n```java\npackage com.ajream.config;\n\nimport com.ajream.pojo.Person;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class MyConfig {\n\n @Bean\n //相当于\n public Person getPerson1(){ //方法名就是bean的id\n return new Person();\n }\n}\n\n```\n\nPerson类:\n\n```java\npackage com.ajream.pojo;\n\nimport org.springframework.beans.factory.annotation.Value;\n\npublic class Person {\n public String name;\n\n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n}\n\n```\n\nMyTest类\n\n\n\n![image-20210801151413775](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-14-18_image-20210801151413775.png)\n\n\n\n这里不使用xml配置文件了,使用Java配置类 `MyConfig`\n\n```java\nimport com.ajream.config.MyConfig;\nimport com.ajream.pojo.Person;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\n\n\npublic class MyTest {\n\n @Test\n public void test1() {\n //加载配置类\n ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);\n\n Person p = context.getBean(\"getPerson1\", Person.class);\n\n System.out.println(p.getName());\n\n }\n}\n\n```\n\n\n\n由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:\n\n![image-20210801155244594](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-52-49_image-20210801155244594.png)","source":"_posts/Spring/11-使用注解开发.md","raw":"---\ntitle: Spring(11)-使用注解开发\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 38b3e585\ndate: 2021-07-28 20:55:13\ndescription: spring使用注解开发,一种还要依赖xml配置文件,另一种可以完全抛弃xml配置文件,使用纯Java来完成属性注入\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 使用注解开发\n\n## 依然使用xml配置文件来管理\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master3/)\n\nspring4之后,要使用注解开发,必须保证aop的包导入了\n\n![image-20210801135559755](https://gitee.com/ajream/images/raw/master/img/2021-08-0113-56-04_image-20210801135559755.png)\n\n使用注解开发,要导入context约束,提供注解的支持\n\n```xml\n\n\n\n \n\n \n \n\n```\n\n\n\n1. bean\n\n ```java\n @component:\n //组件,在创建的类前面加上这个注解,说明这个类已经被spring管理了,相当于:\n //\n //没有参数时,只能以getBean(\"类名的小写字母\") 来获取bean\n @component(value=\"xxx\")\n //有参数时,可以用getBean(\"xxx\") 来获取bean\n ```\n\n Person类:\n\n ```java\n package com.ajream.pojo;\n \n import org.springframework.stereotype.Component;\n \n @Component\t\t\t\t//没有参数时,只能以getBean(\"person\") 来获取bean\n public class Person {\n public String name = \"张三\";\n }\n \n ```\n\n \n\n2. 属性注入\n\n\t ```java\n @Value(\"李四\"):\n //相当于\n ```\n\n \n\n ```java\n \n @Component\n public class Person {\n \n @Value(\"李四\")\t\t//注入属性值\n public String name;\n }\n ```\n\n 也可以在setter方法前使用:\n\n ```java\n @Component\n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n \n\n4. 衍生注解\n\n @Component有几个衍生注解,在web中一般按照mvc三层架构划分:\n\n - dao层:@Repository\n - service层:@Service\n - Controller层:@Controller\n\n 这4个注解功能一样的\n\n5. 作用域scope\n\n 单例\n\n ```java\n @Component\n @Scope(value = \"singleton\") \n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n 原型\n\n ```java\n @Scope(value = \"prototype\") \n ```\n\n6. 小结:\n\n xml与注解\n\n - xml:万能\n\n - 注解:不是自己的类用不了,维护相对复杂\n\n xml与注解最佳实践:\n\n - xml用来管理bean\n\n - 注解只负责属性注入\n\n - 注意:要让注解生效,必须开启注解的支持\n\n ```xml\n \n \n ```\n\n \n\n## 使用Java的方式配置spring\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master4/)\n\n在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置\n\n用MyConfig类来代替beans.xml:\n\n```java\npackage com.ajream.config;\n\nimport com.ajream.pojo.Person;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class MyConfig {\n\n @Bean\n //相当于\n public Person getPerson1(){ //方法名就是bean的id\n return new Person();\n }\n}\n\n```\n\nPerson类:\n\n```java\npackage com.ajream.pojo;\n\nimport org.springframework.beans.factory.annotation.Value;\n\npublic class Person {\n public String name;\n\n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n}\n\n```\n\nMyTest类\n\n\n\n![image-20210801151413775](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-14-18_image-20210801151413775.png)\n\n\n\n这里不使用xml配置文件了,使用Java配置类 `MyConfig`\n\n```java\nimport com.ajream.config.MyConfig;\nimport com.ajream.pojo.Person;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\n\n\npublic class MyTest {\n\n @Test\n public void test1() {\n //加载配置类\n ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);\n\n Person p = context.getBean(\"getPerson1\", Person.class);\n\n System.out.println(p.getName());\n\n }\n}\n\n```\n\n\n\n由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:\n\n![image-20210801155244594](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-52-49_image-20210801155244594.png)","slug":"Spring/11-使用注解开发","published":1,"updated":"2021-09-10T03:16:58.357Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p8002cakvedimceatv","content":"

使用注解开发

依然使用xml配置文件来管理

项目地址

\n

spring4之后,要使用注解开发,必须保证aop的包导入了

\n

\"image-20210801135559755\"

\n

使用注解开发,要导入context约束,提供注解的支持

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>
<!-- 指定要扫描的包,即在该包下的注解生效-->
<context:component-scan base-package="com.ajream.pojo"/>

</beans>
\n
    \n
  1. bean

    \n
    1
    2
    3
    4
    5
    6
    @component
    //组件,在创建的类前面加上这个注解,说明这个类已经被spring管理了,相当于:
    //<bean id="person" class="com.ajream.pojo.Person">
    //没有参数时,只能以getBean("类名的小写字母") 来获取bean
    @component(value="xxx")
    //有参数时,可以用getBean("xxx") 来获取bean
    \n

    Person类:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.ajream.pojo;

    import org.springframework.stereotype.Component;

    @Component\t\t\t\t//没有参数时,只能以getBean("person") 来获取bean
    public class Person {
    public String name = "张三";
    }

    \n
  2. \n
\n
    \n
  1. 属性注入

    \n
    1
    2
    @Value("李四")
    //相当于<property name="name" value="李四" />
    \n
  2. \n
\n
1
2
3
4
5
6
7

@Component
public class Person {

@Value("李四")\t\t//注入属性值
public String name;
}
\n

也可以在setter方法前使用:

\n
1
2
3
4
5
6
7
8
9
@Component
public class Person {
public String name;

@Value("李四")
public void setName(String name) {
this.name = name;
}
}
\n
    \n
  1. 衍生注解

    \n

    @Component有几个衍生注解,在web中一般按照mvc三层架构划分:

    \n
      \n
    • dao层:@Repository
    • \n
    • service层:@Service
    • \n
    • Controller层:@Controller
    • \n
    \n

    这4个注解功能一样的

    \n
  2. \n
  3. 作用域scope

    \n

    单例

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Component
    @Scope(value = "singleton")
    public class Person {
    public String name;

    @Value("李四")
    public void setName(String name) {
    this.name = name;
    }
    }
    \n

    原型

    \n
    1
    @Scope(value = "prototype")  
    \n
  4. \n
  5. 小结:

    \n

    xml与注解

    \n
      \n
    • xml:万能

      \n
    • \n
    • 注解:不是自己的类用不了,维护相对复杂

      \n
    • \n
    \n

    xml与注解最佳实践:

    \n
      \n
    • xml用来管理bean

      \n
    • \n
    • 注解只负责属性注入

      \n
    • \n
    • 注意:要让注解生效,必须开启注解的支持

      \n
      1
      2
      <context:annotation-config/>
      <context:component-scan base-package="com.ajream.pojo"/>
      \n
    • \n
    \n
  6. \n
\n

使用Java的方式配置spring

项目地址

\n

在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置

\n

用MyConfig类来代替beans.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.config;

import com.ajream.pojo.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

@Bean
//相当于<bean id="getPerson1" class="com.ajream.pojo.Person">
public Person getPerson1(){ //方法名就是bean的id
return new Person();
}
}

\n

Person类:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.pojo;

import org.springframework.beans.factory.annotation.Value;

public class Person {
public String name;

@Value("李四")
public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

\n

MyTest类

\n

\"image-20210801151413775\"

\n

这里不使用xml配置文件了,使用Java配置类 MyConfig

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.ajream.config.MyConfig;
import com.ajream.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class MyTest {

@Test
public void test1() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

Person p = context.getBean("getPerson1", Person.class);

System.out.println(p.getName());

}
}

\n

由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:

\n

\"image-20210801155244594\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

使用注解开发

依然使用xml配置文件来管理

项目地址

\n

spring4之后,要使用注解开发,必须保证aop的包导入了

\n

\"image-20210801135559755\"

\n

使用注解开发,要导入context约束,提供注解的支持

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>
<!-- 指定要扫描的包,即在该包下的注解生效-->
<context:component-scan base-package="com.ajream.pojo"/>

</beans>
\n
    \n
  1. bean

    \n
    1
    2
    3
    4
    5
    6
    @component
    //组件,在创建的类前面加上这个注解,说明这个类已经被spring管理了,相当于:
    //<bean id="person" class="com.ajream.pojo.Person">
    //没有参数时,只能以getBean("类名的小写字母") 来获取bean
    @component(value="xxx")
    //有参数时,可以用getBean("xxx") 来获取bean
    \n

    Person类:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.ajream.pojo;

    import org.springframework.stereotype.Component;

    @Component\t\t\t\t//没有参数时,只能以getBean("person") 来获取bean
    public class Person {
    public String name = "张三";
    }

    \n
  2. \n
\n
    \n
  1. 属性注入

    \n
    1
    2
    @Value("李四")
    //相当于<property name="name" value="李四" />
    \n
  2. \n
\n
1
2
3
4
5
6
7

@Component
public class Person {

@Value("李四")\t\t//注入属性值
public String name;
}
\n

也可以在setter方法前使用:

\n
1
2
3
4
5
6
7
8
9
@Component
public class Person {
public String name;

@Value("李四")
public void setName(String name) {
this.name = name;
}
}
\n
    \n
  1. 衍生注解

    \n

    @Component有几个衍生注解,在web中一般按照mvc三层架构划分:

    \n
      \n
    • dao层:@Repository
    • \n
    • service层:@Service
    • \n
    • Controller层:@Controller
    • \n
    \n

    这4个注解功能一样的

    \n
  2. \n
  3. 作用域scope

    \n

    单例

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Component
    @Scope(value = "singleton")
    public class Person {
    public String name;

    @Value("李四")
    public void setName(String name) {
    this.name = name;
    }
    }
    \n

    原型

    \n
    1
    @Scope(value = "prototype")  
    \n
  4. \n
  5. 小结:

    \n

    xml与注解

    \n
      \n
    • xml:万能

      \n
    • \n
    • 注解:不是自己的类用不了,维护相对复杂

      \n
    • \n
    \n

    xml与注解最佳实践:

    \n
      \n
    • xml用来管理bean

      \n
    • \n
    • 注解只负责属性注入

      \n
    • \n
    • 注意:要让注解生效,必须开启注解的支持

      \n
      1
      2
      <context:annotation-config/>
      <context:component-scan base-package="com.ajream.pojo"/>
      \n
    • \n
    \n
  6. \n
\n

使用Java的方式配置spring

项目地址

\n

在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置

\n

用MyConfig类来代替beans.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.config;

import com.ajream.pojo.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

@Bean
//相当于<bean id="getPerson1" class="com.ajream.pojo.Person">
public Person getPerson1(){ //方法名就是bean的id
return new Person();
}
}

\n

Person类:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.pojo;

import org.springframework.beans.factory.annotation.Value;

public class Person {
public String name;

@Value("李四")
public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

\n

MyTest类

\n

\"image-20210801151413775\"

\n

这里不使用xml配置文件了,使用Java配置类 MyConfig

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.ajream.config.MyConfig;
import com.ajream.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class MyTest {

@Test
public void test1() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

Person p = context.getBean("getPerson1", Person.class);

System.out.println(p.getName());

}
}

\n

由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:

\n

\"image-20210801155244594\"

\n"},{"title":"Spring(13)-动态代理","abbrlink":"26c04051","date":"2021-07-29T07:53:34.000Z","description":"Java动态代理模式举例介绍","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 动态代理\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n底层用到`反射`\n\n- 动态代理与静态代理角色一样\n- 动态代理的代理类是动态生成,并非我们直接写好\n- 分类:基于接口和基于类的\n - 基于接口——JDK动态代理\n - 基于类——cglib\n - Java字节码实现——javasist\n\n\n\n需要了解2个类:\n\n- Proxy:调用`newProxyInstance` 方法用于生成代理角色\n\n- InvocationHandler:重写 `invoke` 方法,指明代理角色要处理的功能,详细[查看项目](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n ```java\n //这是一个用于创建代理角色的类,不是代理类\n public class ProxyInvocationHandler implements InvocationHandler {\n \n //指向被代理的接口(即真实角色)\n private Rent r;\n \n public void setRent(Rent r) {\n this.r = r;\n }\n \n //生成代理角色\n public Object getProxy(){\n return Proxy.newProxyInstance(this.getClass().getClassLoader(), r.getClass().getInterfaces(), this);\n }\n \n @Override\n //处理代理实例(即被代理者),并返回结果\n public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n seeHouse();\n fare();\n Object result = method.invoke(r, args);\n return result;\n }\n \n ```\n\n \n\n\n\n动态代理好处:\n\n- 可以使真实角色的操作更加纯粹\n- 公共交给代理角色,实现业务分工\n- 公共业务扩展时,方便集中管理\n- 一个动态代理类可以代理多个类(只要实现的是同一个接口即可)\n\n\n\n","source":"_posts/Spring/13-动态代理.md","raw":"---\ntitle: Spring(13)-动态代理\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 26c04051\ndate: 2021-07-29 15:53:34\ndescription: Java动态代理模式举例介绍\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 动态代理\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n底层用到`反射`\n\n- 动态代理与静态代理角色一样\n- 动态代理的代理类是动态生成,并非我们直接写好\n- 分类:基于接口和基于类的\n - 基于接口——JDK动态代理\n - 基于类——cglib\n - Java字节码实现——javasist\n\n\n\n需要了解2个类:\n\n- Proxy:调用`newProxyInstance` 方法用于生成代理角色\n\n- InvocationHandler:重写 `invoke` 方法,指明代理角色要处理的功能,详细[查看项目](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n ```java\n //这是一个用于创建代理角色的类,不是代理类\n public class ProxyInvocationHandler implements InvocationHandler {\n \n //指向被代理的接口(即真实角色)\n private Rent r;\n \n public void setRent(Rent r) {\n this.r = r;\n }\n \n //生成代理角色\n public Object getProxy(){\n return Proxy.newProxyInstance(this.getClass().getClassLoader(), r.getClass().getInterfaces(), this);\n }\n \n @Override\n //处理代理实例(即被代理者),并返回结果\n public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n seeHouse();\n fare();\n Object result = method.invoke(r, args);\n return result;\n }\n \n ```\n\n \n\n\n\n动态代理好处:\n\n- 可以使真实角色的操作更加纯粹\n- 公共交给代理角色,实现业务分工\n- 公共业务扩展时,方便集中管理\n- 一个动态代理类可以代理多个类(只要实现的是同一个接口即可)\n\n\n\n","slug":"Spring/13-动态代理","published":1,"updated":"2021-09-10T03:16:40.037Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p9002fakve7ps4flkp","content":"

动态代理

项目地址

\n

底层用到反射

\n
    \n
  • 动态代理与静态代理角色一样
  • \n
  • 动态代理的代理类是动态生成,并非我们直接写好
  • \n
  • 分类:基于接口和基于类的
      \n
    • 基于接口——JDK动态代理
    • \n
    • 基于类——cglib
    • \n
    • Java字节码实现——javasist
    • \n
    \n
  • \n
\n

需要了解2个类:

\n
    \n
  • Proxy:调用newProxyInstance 方法用于生成代理角色

    \n
  • \n
  • InvocationHandler:重写 invoke 方法,指明代理角色要处理的功能,详细查看项目

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //这是一个用于创建代理角色的类,不是代理类
    public class ProxyInvocationHandler implements InvocationHandler {

    //指向被代理的接口(即真实角色)
    private Rent r;

    public void setRent(Rent r) {
    this.r = r;
    }

    //生成代理角色
    public Object getProxy(){
    return Proxy.newProxyInstance(this.getClass().getClassLoader(), r.getClass().getInterfaces(), this);
    }

    @Override
    //处理代理实例(即被代理者),并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    seeHouse();
    fare();
    Object result = method.invoke(r, args);
    return result;
    }

    \n
  • \n
\n

动态代理好处:

\n
    \n
  • 可以使真实角色的操作更加纯粹
  • \n
  • 公共交给代理角色,实现业务分工
  • \n
  • 公共业务扩展时,方便集中管理
  • \n
  • 一个动态代理类可以代理多个类(只要实现的是同一个接口即可)
  • \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

动态代理

项目地址

\n

底层用到反射

\n
    \n
  • 动态代理与静态代理角色一样
  • \n
  • 动态代理的代理类是动态生成,并非我们直接写好
  • \n
  • 分类:基于接口和基于类的
      \n
    • 基于接口——JDK动态代理
    • \n
    • 基于类——cglib
    • \n
    • Java字节码实现——javasist
    • \n
    \n
  • \n
\n

需要了解2个类:

\n
    \n
  • Proxy:调用newProxyInstance 方法用于生成代理角色

    \n
  • \n
  • InvocationHandler:重写 invoke 方法,指明代理角色要处理的功能,详细查看项目

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //这是一个用于创建代理角色的类,不是代理类
    public class ProxyInvocationHandler implements InvocationHandler {

    //指向被代理的接口(即真实角色)
    private Rent r;

    public void setRent(Rent r) {
    this.r = r;
    }

    //生成代理角色
    public Object getProxy(){
    return Proxy.newProxyInstance(this.getClass().getClassLoader(), r.getClass().getInterfaces(), this);
    }

    @Override
    //处理代理实例(即被代理者),并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    seeHouse();
    fare();
    Object result = method.invoke(r, args);
    return result;
    }

    \n
  • \n
\n

动态代理好处:

\n
    \n
  • 可以使真实角色的操作更加纯粹
  • \n
  • 公共交给代理角色,实现业务分工
  • \n
  • 公共业务扩展时,方便集中管理
  • \n
  • 一个动态代理类可以代理多个类(只要实现的是同一个接口即可)
  • \n
\n"},{"title":"Spring(14)-springAOP","abbrlink":"a1a1eb5","date":"2021-07-30T05:50:41.000Z","description":"springAOP使用了动态代理模式,其基本使用进入文章详细了解","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# Spring Aop\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master)\n\n## aop实现方式1(实现spring接口)\n\n### 导包\n\n使用之前要导入依赖包\n\n![image-20210803155603975](https://gitee.com/ajream/images/raw/master/img/2021-08-0315-56-08_image-20210803155603975.png)\n\n```xml\n\n \n org.springframework\n spring-context\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.12\n \n \n org.aspectj\n aspectjweaver\n 1.9.6\n \n\n```\n\n\n\n项目结构:\n\n```\nspring1-aop\n├───.idea\n├───src\n│ ├───main\n│ │ ├───java\n│ │ │ └───com\n│ │ │ └───ajream\n│ │ │ ├───log\n│ │ │ └───service\n│ │ └───resources\n│ └───test\n └───java\n```\n\n\n\n### 业务模块service\n\n被代理的接口,包含了要实现的具体功能\n\nUserService.java接口:\n\n```java\npackage com.ajream.service;\n\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n```\n\nUserServiceImpl.java\n\n```java\npackage com.ajream.service;\n\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n### 代理模块\n\n在原有基础上扩展一些功能\n\nLog.java (在实现某个业务(调用某个方法)前,打印一些相关信息)\n\n> 说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.MethodBeforeAdvice;\n\nimport java.lang.reflect.Method;\n\npublic class Log implements MethodBeforeAdvice {\n @Override\n public void before(Method method, Object[] args, Object target) throws Throwable {\n assert target != null;\n System.out.println(target.getClass().getName() + \"的\" + method.getName() + \"方法被执行了\");\n }\n}\n\n```\n\n\n\nAfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息) \n\n> 说明:重写AfterReturningAdvice的 `afterReturning` 方法,说明在切入点之后执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.AfterReturningAdvice;\nimport java.lang.reflect.Method;\n\npublic class AfterLog implements AfterReturningAdvice {\n @Override\n public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {\n System.out.println(\"执行了\" +method.getName()+ \"方法,返回值为:\"+returnValue);\n }\n}\n\n```\n\n\n\nApplicationContext.xml 配置文件\n\n```xml\n\n\n\n \n \n \n\n \n\n \n\n\n \n \n\n \n\n\n```\n\n> 说明:\n>\n> 1. 注意添加aop支持\n>\n> ```\n> xmlns:aop=\"http://www.springframework.org/schema/aop\"\n> http://www.springframework.org/schema/aop\n> https://www.springframework.org/schema/aop/spring-aop.xsd\n> ```\n>\n> 2. aop配置(使用原生spring api接口)\n>\n> ```xml\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n### 测试\n\nMyTest.java测试\n\n```java\nimport com.ajream.service.UserService;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n @Test\n public void test(){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"ApplicationContext.xml\");\n\n //动态代理的是接口\n UserService userService = context.getBean(\"userService\", UserService.class);\n userService.add();\n }\n}\n\n```\n\n\n\n测试结果:\n\n![image-20210808163457649](https://gitee.com/ajream/images/raw/master/img/2021-08-0816-35-03_image-20210808163457649.png)\n\n\n\n## Aop实现方式2(自定义切入类)\n\n主要是aop配置方式不同\n\n之前是使用原生spring api方式,分别实现了`MethodBeforeAdvice`和`AfterReturningAdvice`这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:\n\n```xml\n\n \n \n \n \n \n \n \n\n```\n\n---\n\n现在改用自定义切入面方式:\n\n首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)\n\n\n\n```java\npackage com.ajream.diy;\n\n//自定义切入面\npublic class DiyPointCut {\n public void before(){\n System.out.println(\"=====方法执行前======\");\n }\n\n public void after(){\n System.out.println(\"======方法执行后======\");\n }\n}\n\n```\n\naop配置:\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n## aop实现方式3(使用注解开发)\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master1)\n\n首先开启aop注解支持\n\n```xml\n\n\n\n \n\n\n \n\n \n\n```\n\n\n\n添加AnnotationPointCut.java类\n\n```java\npackage com.ajream.diy;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.After;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\n\n@Aspect //标注这是一个切面\npublic class AnnotationPointCut {\n\n @Before(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //切入位置\n public void before(){\n System.out.println(\"=====方法执行前=====\");\n }\n\n @After(\"execution(* com.ajream.service.UserServiceImpl.*(..))\")\n public void after(){\n System.out.println(\"=====方法执行后=====\");\n }\n\n @Around(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //环绕增强\n public void around(ProceedingJoinPoint jp) throws Throwable {\n System.out.println(\"环绕前\");\n Object proceed = jp.proceed();// 执行方法\n System.out.println(\"执行的方法签名:\" + jp.getSignature());\n System.out.println(\"环绕后\");\n\n }\n}\n\n```\n\n> 注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 `@Before` `@After` 这2个注解的内容,所以输出结果如下:\n>\n> ![image-20210808173720981](https://gitee.com/ajream/images/raw/master/img/2021-08-08 17-37-27_image-20210808173720981.png)\n\n","source":"_posts/Spring/14-springAOP.md","raw":"---\ntitle: Spring(14)-springAOP\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: a1a1eb5\ndate: 2021-07-30 13:50:41\ndescription: springAOP使用了动态代理模式,其基本使用进入文章详细了解\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# Spring Aop\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master)\n\n## aop实现方式1(实现spring接口)\n\n### 导包\n\n使用之前要导入依赖包\n\n![image-20210803155603975](https://gitee.com/ajream/images/raw/master/img/2021-08-0315-56-08_image-20210803155603975.png)\n\n```xml\n\n \n org.springframework\n spring-context\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.12\n \n \n org.aspectj\n aspectjweaver\n 1.9.6\n \n\n```\n\n\n\n项目结构:\n\n```\nspring1-aop\n├───.idea\n├───src\n│ ├───main\n│ │ ├───java\n│ │ │ └───com\n│ │ │ └───ajream\n│ │ │ ├───log\n│ │ │ └───service\n│ │ └───resources\n│ └───test\n └───java\n```\n\n\n\n### 业务模块service\n\n被代理的接口,包含了要实现的具体功能\n\nUserService.java接口:\n\n```java\npackage com.ajream.service;\n\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n```\n\nUserServiceImpl.java\n\n```java\npackage com.ajream.service;\n\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n### 代理模块\n\n在原有基础上扩展一些功能\n\nLog.java (在实现某个业务(调用某个方法)前,打印一些相关信息)\n\n> 说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.MethodBeforeAdvice;\n\nimport java.lang.reflect.Method;\n\npublic class Log implements MethodBeforeAdvice {\n @Override\n public void before(Method method, Object[] args, Object target) throws Throwable {\n assert target != null;\n System.out.println(target.getClass().getName() + \"的\" + method.getName() + \"方法被执行了\");\n }\n}\n\n```\n\n\n\nAfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息) \n\n> 说明:重写AfterReturningAdvice的 `afterReturning` 方法,说明在切入点之后执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.AfterReturningAdvice;\nimport java.lang.reflect.Method;\n\npublic class AfterLog implements AfterReturningAdvice {\n @Override\n public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {\n System.out.println(\"执行了\" +method.getName()+ \"方法,返回值为:\"+returnValue);\n }\n}\n\n```\n\n\n\nApplicationContext.xml 配置文件\n\n```xml\n\n\n\n \n \n \n\n \n\n \n\n\n \n \n\n \n\n\n```\n\n> 说明:\n>\n> 1. 注意添加aop支持\n>\n> ```\n> xmlns:aop=\"http://www.springframework.org/schema/aop\"\n> http://www.springframework.org/schema/aop\n> https://www.springframework.org/schema/aop/spring-aop.xsd\n> ```\n>\n> 2. aop配置(使用原生spring api接口)\n>\n> ```xml\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n### 测试\n\nMyTest.java测试\n\n```java\nimport com.ajream.service.UserService;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n @Test\n public void test(){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"ApplicationContext.xml\");\n\n //动态代理的是接口\n UserService userService = context.getBean(\"userService\", UserService.class);\n userService.add();\n }\n}\n\n```\n\n\n\n测试结果:\n\n![image-20210808163457649](https://gitee.com/ajream/images/raw/master/img/2021-08-0816-35-03_image-20210808163457649.png)\n\n\n\n## Aop实现方式2(自定义切入类)\n\n主要是aop配置方式不同\n\n之前是使用原生spring api方式,分别实现了`MethodBeforeAdvice`和`AfterReturningAdvice`这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:\n\n```xml\n\n \n \n \n \n \n \n \n\n```\n\n---\n\n现在改用自定义切入面方式:\n\n首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)\n\n\n\n```java\npackage com.ajream.diy;\n\n//自定义切入面\npublic class DiyPointCut {\n public void before(){\n System.out.println(\"=====方法执行前======\");\n }\n\n public void after(){\n System.out.println(\"======方法执行后======\");\n }\n}\n\n```\n\naop配置:\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n## aop实现方式3(使用注解开发)\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master1)\n\n首先开启aop注解支持\n\n```xml\n\n\n\n \n\n\n \n\n \n\n```\n\n\n\n添加AnnotationPointCut.java类\n\n```java\npackage com.ajream.diy;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.After;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\n\n@Aspect //标注这是一个切面\npublic class AnnotationPointCut {\n\n @Before(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //切入位置\n public void before(){\n System.out.println(\"=====方法执行前=====\");\n }\n\n @After(\"execution(* com.ajream.service.UserServiceImpl.*(..))\")\n public void after(){\n System.out.println(\"=====方法执行后=====\");\n }\n\n @Around(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //环绕增强\n public void around(ProceedingJoinPoint jp) throws Throwable {\n System.out.println(\"环绕前\");\n Object proceed = jp.proceed();// 执行方法\n System.out.println(\"执行的方法签名:\" + jp.getSignature());\n System.out.println(\"环绕后\");\n\n }\n}\n\n```\n\n> 注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 `@Before` `@After` 这2个注解的内容,所以输出结果如下:\n>\n> ![image-20210808173720981](https://gitee.com/ajream/images/raw/master/img/2021-08-08 17-37-27_image-20210808173720981.png)\n\n","slug":"Spring/14-springAOP","published":1,"updated":"2021-09-10T03:16:18.937Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pb002jakvefzhpc3tk","content":"

Spring Aop

项目地址

\n

aop实现方式1(实现spring接口)

导包

使用之前要导入依赖包

\n

\"image-20210803155603975\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
\n

项目结构:

\n
1
2
3
4
5
6
7
8
9
10
11
12
spring1-aop
├───.idea
├───src
│ ├───main
│ │ ├───java
│ │ │ └───com
│ │ │ └───ajream
│ │ │ ├───log
│ │ │ └───service
│ │ └───resources
│ └───test
└───java
\n

业务模块service

被代理的接口,包含了要实现的具体功能

\n

UserService.java接口:

\n
1
2
3
4
5
6
7
8
package com.ajream.service;

public interface UserService {
void add();
void delete();
void update();
void query();
}
\n

UserServiceImpl.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ajream.service;

public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加一个用户");
}

@Override
public void delete() {
System.out.println("删除一个用户");
}

@Override
public void update() {
System.out.println("修改一个用户");
}

@Override
public void query() {
System.out.println("查询一个用户");
}
}

\n

代理模块

在原有基础上扩展一些功能

\n

Log.java (在实现某个业务(调用某个方法)前,打印一些相关信息)

\n
\n

说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.ajream.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
assert target != null;
System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}

\n

AfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息)

\n
\n

说明:重写AfterReturningAdvice的 afterReturning 方法,说明在切入点之后执行这个方法

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
package com.ajream.log;

import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" +method.getName()+ "方法,返回值为:"+returnValue);
}
}

\n

ApplicationContext.xml 配置文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
>

<bean id="userService" class="com.ajream.service.UserServiceImpl" />
<bean id="log" class="com.ajream.log.Log" />
<bean id="afterLog" class="com.ajream.log.AfterLog" />

<aop:config>
<!-- 定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
<aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

<!-- 执行环绕增加-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
<aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

</aop:config>

</beans>
\n
\n

说明:

\n
    \n
  1. 注意添加aop支持

    \n
    1
    2
    3
    xmlns:aop="http://www.springframework.org/schema/aop"
    http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd
    \n
  2. \n
  3. aop配置(使用原生spring api接口)

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <aop:config>
    <!--定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
    <aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

    <!--执行环绕增加,即在切入点前后执行下面的操作-->
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
    <aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

    </aop:config>
    \n
  4. \n
\n
\n

测试

MyTest.java测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.ajream.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");

//动态代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}

\n

测试结果:

\n

\"image-20210808163457649\"

\n

Aop实现方式2(自定义切入类)

主要是aop配置方式不同

\n

之前是使用原生spring api方式,分别实现了MethodBeforeAdviceAfterReturningAdvice这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:

\n
1
2
3
4
5
6
7
8
9
<aop:config>
<!--定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
<aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

<!--执行环绕增加,即在切入点前后执行下面的操作-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
<aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

</aop:config>
\n
\n

现在改用自定义切入面方式:

\n

首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ajream.diy;

//自定义切入面
public class DiyPointCut {
public void before(){
System.out.println("=====方法执行前======");
}

public void after(){
System.out.println("======方法执行后======");
}
}

\n

aop配置:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<!--方式2:自定义类-->
<bean id="diy" class="com.ajream.diy.DiyPointCut" />

<aop:config>
<!--定义切面,ref为要引用的类-->
<aop:aspect ref="diy" >
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>
<!-- aop:before表示在切入点point之前执行切面的before方法-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point" />
</aop:aspect>
</aop:config>
\n

aop实现方式3(使用注解开发)

项目地址

\n

首先开启aop注解支持

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
>

<bean id="userService" class="com.ajream.service.UserServiceImpl" />

<!--方式3,注解方式-->
<bean id="annotationPointCut" class="com.ajream.diy.AnnotationPointCut" />
<!--开启aop注解支持-->
<aop:aspectj-autoproxy />
</beans>
\n

添加AnnotationPointCut.java类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.ajream.diy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect //标注这是一个切面
public class AnnotationPointCut {

@Before("execution(* com.ajream.service.UserServiceImpl.*(..))") //切入位置
public void before(){
System.out.println("=====方法执行前=====");
}

@After("execution(* com.ajream.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====方法执行后=====");
}

@Around("execution(* com.ajream.service.UserServiceImpl.*(..))") //环绕增强
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Object proceed = jp.proceed();// 执行方法
System.out.println("执行的方法签名:" + jp.getSignature());
System.out.println("环绕后");

}
}

\n
\n

注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 @Before @After 这2个注解的内容,所以输出结果如下:

\n

\"image-20210808173720981\"

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Spring Aop

项目地址

\n

aop实现方式1(实现spring接口)

导包

使用之前要导入依赖包

\n

\"image-20210803155603975\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
\n

项目结构:

\n
1
2
3
4
5
6
7
8
9
10
11
12
spring1-aop
├───.idea
├───src
│ ├───main
│ │ ├───java
│ │ │ └───com
│ │ │ └───ajream
│ │ │ ├───log
│ │ │ └───service
│ │ └───resources
│ └───test
└───java
\n

业务模块service

被代理的接口,包含了要实现的具体功能

\n

UserService.java接口:

\n
1
2
3
4
5
6
7
8
package com.ajream.service;

public interface UserService {
void add();
void delete();
void update();
void query();
}
\n

UserServiceImpl.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ajream.service;

public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加一个用户");
}

@Override
public void delete() {
System.out.println("删除一个用户");
}

@Override
public void update() {
System.out.println("修改一个用户");
}

@Override
public void query() {
System.out.println("查询一个用户");
}
}

\n

代理模块

在原有基础上扩展一些功能

\n

Log.java (在实现某个业务(调用某个方法)前,打印一些相关信息)

\n
\n

说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.ajream.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
assert target != null;
System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}

\n

AfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息)

\n
\n

说明:重写AfterReturningAdvice的 afterReturning 方法,说明在切入点之后执行这个方法

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
package com.ajream.log;

import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" +method.getName()+ "方法,返回值为:"+returnValue);
}
}

\n

ApplicationContext.xml 配置文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
>

<bean id="userService" class="com.ajream.service.UserServiceImpl" />
<bean id="log" class="com.ajream.log.Log" />
<bean id="afterLog" class="com.ajream.log.AfterLog" />

<aop:config>
<!-- 定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
<aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

<!-- 执行环绕增加-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
<aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

</aop:config>

</beans>
\n
\n

说明:

\n
    \n
  1. 注意添加aop支持

    \n
    1
    2
    3
    xmlns:aop="http://www.springframework.org/schema/aop"
    http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd
    \n
  2. \n
  3. aop配置(使用原生spring api接口)

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <aop:config>
    <!--定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
    <aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

    <!--执行环绕增加,即在切入点前后执行下面的操作-->
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
    <aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

    </aop:config>
    \n
  4. \n
\n
\n

测试

MyTest.java测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.ajream.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");

//动态代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}

\n

测试结果:

\n

\"image-20210808163457649\"

\n

Aop实现方式2(自定义切入类)

主要是aop配置方式不同

\n

之前是使用原生spring api方式,分别实现了MethodBeforeAdviceAfterReturningAdvice这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:

\n
1
2
3
4
5
6
7
8
9
<aop:config>
<!--定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
<aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

<!--执行环绕增加,即在切入点前后执行下面的操作-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
<aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

</aop:config>
\n
\n

现在改用自定义切入面方式:

\n

首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ajream.diy;

//自定义切入面
public class DiyPointCut {
public void before(){
System.out.println("=====方法执行前======");
}

public void after(){
System.out.println("======方法执行后======");
}
}

\n

aop配置:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<!--方式2:自定义类-->
<bean id="diy" class="com.ajream.diy.DiyPointCut" />

<aop:config>
<!--定义切面,ref为要引用的类-->
<aop:aspect ref="diy" >
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>
<!-- aop:before表示在切入点point之前执行切面的before方法-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point" />
</aop:aspect>
</aop:config>
\n

aop实现方式3(使用注解开发)

项目地址

\n

首先开启aop注解支持

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
>

<bean id="userService" class="com.ajream.service.UserServiceImpl" />

<!--方式3,注解方式-->
<bean id="annotationPointCut" class="com.ajream.diy.AnnotationPointCut" />
<!--开启aop注解支持-->
<aop:aspectj-autoproxy />
</beans>
\n

添加AnnotationPointCut.java类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.ajream.diy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect //标注这是一个切面
public class AnnotationPointCut {

@Before("execution(* com.ajream.service.UserServiceImpl.*(..))") //切入位置
public void before(){
System.out.println("=====方法执行前=====");
}

@After("execution(* com.ajream.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====方法执行后=====");
}

@Around("execution(* com.ajream.service.UserServiceImpl.*(..))") //环绕增强
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Object proceed = jp.proceed();// 执行方法
System.out.println("执行的方法签名:" + jp.getSignature());
System.out.println("环绕后");

}
}

\n
\n

注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 @Before @After 这2个注解的内容,所以输出结果如下:

\n

\"image-20210808173720981\"

\n
\n"},{"title":"Spring(2)-SpringIOC","abbrlink":"752d4bdb","date":"2021-07-24T07:57:27.000Z","description":"springIOC的结构与内容","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# SpringIOC\n\n\n\n![Spring+IOC](https://gitee.com/ajream/images/raw/master/img/2021-07-3021-11-35_Spring+IOC.svg)\n\n\n\n## IOC理论推导\n\n1. userDao接口\n\n userDaoImpl实现类(Mysql, Oracle, ...)\n\n2. userService业务接口\n\n userServiceImpl 业务实现\n\n\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n\n\n","source":"_posts/Spring/2-SpringIOC.md","raw":"---\ntitle: Spring(2)-SpringIOC\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 752d4bdb\ndate: 2021-07-24 15:57:27\ndescription: springIOC的结构与内容\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# SpringIOC\n\n\n\n![Spring+IOC](https://gitee.com/ajream/images/raw/master/img/2021-07-3021-11-35_Spring+IOC.svg)\n\n\n\n## IOC理论推导\n\n1. userDao接口\n\n userDaoImpl实现类(Mysql, Oracle, ...)\n\n2. userService业务接口\n\n userServiceImpl 业务实现\n\n\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n\n\n","slug":"Spring/2-SpringIOC","published":1,"updated":"2021-09-10T03:18:09.108Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pc002makvec2g3cfzf","content":"

SpringIOC

\"Spring+IOC\"

\n

IOC理论推导

    \n
  1. userDao接口

    \n

    userDaoImpl实现类(Mysql, Oracle, …)

    \n
  2. \n
  3. userService业务接口

    \n

    userServiceImpl 业务实现

    \n
  4. \n
\n

\"ioc\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

SpringIOC

\"Spring+IOC\"

\n

IOC理论推导

    \n
  1. userDao接口

    \n

    userDaoImpl实现类(Mysql, Oracle, …)

    \n
  2. \n
  3. userService业务接口

    \n

    userServiceImpl 业务实现

    \n
  4. \n
\n

\"ioc\"

\n"},{"title":"Spring(4)-HelloSpring程序","abbrlink":"59e405f3","date":"2021-07-25T04:48:31.000Z","description":"第一个spring程序","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n# HelloSpring项目\n\n\n\n## 使用maven创建\n\n\n\n创建后目录结构如下:\n\n![image-20210729125943211](https://gitee.com/ajream/images/raw/master/img/2021-07-2912-59-47_image-20210729125943211.png)\n\n\n\n\n\npom.xml:\n\n```xml\n\n\n 4.0.0\n\n org.ajream\n hello_spring\n 1.0-SNAPSHOT\n \n \n org.springframework\n spring-context\n 5.2.9.RELEASE\n test\n \n \n\n \n 8\n 8\n \n\n\n```\n\n\n\n## 代码\n\n1. 在java文件夹下创建`com.xxx.pojo.Hello`类\n\n![image-20210729130640647](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-06-43_image-20210729130640647.png)\n\n```java\npackage com.ajream.pojo;\n\npublic class Hello {\n private String str1; //先不管属性名为什么是str1\n\n public void setStr2(String s){ //先不管为什么函数名设置为setStr2\n this.str1 = s;\n }\n\n public void printStr(){\n System.out.println(\"Hello:->\" + str1);\n }\n}\n\n```\n\n\n\n2. 在resources下创建`beans.xml`配置文件\n\n```xml\n\n\n\n \n\n \n\n \n\n```\n\n> 该文件内容模板可以从spring[官网](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core)中找到\n>\n> ![image-20210729131316578](https://gitee.com/ajream/images/raw/master/img/2021-07-29 13-13-19_image-20210729131316578.png)\n>\n> ```xml\n> \n> xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n> xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n> https://www.springframework.org/schema/beans/spring-beans.xsd\">\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n3. 在test/java下创建MyTest类来进行测试输出:\n\n```java\nimport com.ajream.pojo.Hello;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n public static void main(String[] args){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\"); //加载xml配置文件\n Hello h = (Hello) context.getBean(\"helloworld\");\n h.printStr();\n }\n}\n\n```\n\n输出:\n\n![image-20210729131704285](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-17-08_image-20210729131704285.png)\n\n\n\n\n\n## 三者之间的关系\n\n![image-20210729134624753](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-46-29_image-20210729134624753.png)\n\n\n\n执行流程:\n\n1. 获取 `beans.xml` 配置文件信息\n2. 根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法\n3. 调用函数printStr\n\n\n\n\n\n","source":"_posts/Spring/4-HelloSpring程序.md","raw":"---\ntitle: Spring(4)-HelloSpring程序\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 59e405f3\ndate: 2021-07-25 12:48:31\ndescription: 第一个spring程序\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n# HelloSpring项目\n\n\n\n## 使用maven创建\n\n\n\n创建后目录结构如下:\n\n![image-20210729125943211](https://gitee.com/ajream/images/raw/master/img/2021-07-2912-59-47_image-20210729125943211.png)\n\n\n\n\n\npom.xml:\n\n```xml\n\n\n 4.0.0\n\n org.ajream\n hello_spring\n 1.0-SNAPSHOT\n \n \n org.springframework\n spring-context\n 5.2.9.RELEASE\n test\n \n \n\n \n 8\n 8\n \n\n\n```\n\n\n\n## 代码\n\n1. 在java文件夹下创建`com.xxx.pojo.Hello`类\n\n![image-20210729130640647](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-06-43_image-20210729130640647.png)\n\n```java\npackage com.ajream.pojo;\n\npublic class Hello {\n private String str1; //先不管属性名为什么是str1\n\n public void setStr2(String s){ //先不管为什么函数名设置为setStr2\n this.str1 = s;\n }\n\n public void printStr(){\n System.out.println(\"Hello:->\" + str1);\n }\n}\n\n```\n\n\n\n2. 在resources下创建`beans.xml`配置文件\n\n```xml\n\n\n\n \n\n \n\n \n\n```\n\n> 该文件内容模板可以从spring[官网](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core)中找到\n>\n> ![image-20210729131316578](https://gitee.com/ajream/images/raw/master/img/2021-07-29 13-13-19_image-20210729131316578.png)\n>\n> ```xml\n> \n> xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n> xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n> https://www.springframework.org/schema/beans/spring-beans.xsd\">\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n3. 在test/java下创建MyTest类来进行测试输出:\n\n```java\nimport com.ajream.pojo.Hello;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n public static void main(String[] args){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\"); //加载xml配置文件\n Hello h = (Hello) context.getBean(\"helloworld\");\n h.printStr();\n }\n}\n\n```\n\n输出:\n\n![image-20210729131704285](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-17-08_image-20210729131704285.png)\n\n\n\n\n\n## 三者之间的关系\n\n![image-20210729134624753](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-46-29_image-20210729134624753.png)\n\n\n\n执行流程:\n\n1. 获取 `beans.xml` 配置文件信息\n2. 根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法\n3. 调用函数printStr\n\n\n\n\n\n","slug":"Spring/4-HelloSpring程序","published":1,"updated":"2021-09-10T03:18:01.840Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pd002qakve0ic15g6q","content":"

HelloSpring项目

使用maven创建

创建后目录结构如下:

\n

\"image-20210729125943211\"

\n

pom.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.ajream</groupId>
<artifactId>hello_spring</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

</project>
\n

代码

    \n
  1. 在java文件夹下创建com.xxx.pojo.Hello
  2. \n
\n

\"image-20210729130640647\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.ajream.pojo;

public class Hello {
private String str1; //先不管属性名为什么是str1

public void setStr2(String s){ //先不管为什么函数名设置为setStr2
this.str1 = s;
}

public void printStr(){
System.out.println("Hello:->" + str1);
}
}

\n
    \n
  1. 在resources下创建beans.xml配置文件
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="helloworld" class="com.ajream.pojo.Hello">
<!-- 通过name来识别出调用了哪个函数, value为传入参数-->
<property name="str2" value="spring" /> <!--这行代码表示调用函数 setStr2, 传入参数为字符串"spring" -->

</bean>
</beans>
\n
\n

该文件内容模板可以从spring官网中找到

\n

\"image-20210729131316578\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions go here -->

</beans>
\n
\n
    \n
  1. 在test/java下创建MyTest类来进行测试输出:
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import com.ajream.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //加载xml配置文件
Hello h = (Hello) context.getBean("helloworld");
h.printStr();
}
}

\n

输出:

\n

\"image-20210729131704285\"

\n

三者之间的关系

\"image-20210729134624753\"

\n

执行流程:

\n
    \n
  1. 获取 beans.xml 配置文件信息
  2. \n
  3. 根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法
  4. \n
  5. 调用函数printStr
  6. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

HelloSpring项目

使用maven创建

创建后目录结构如下:

\n

\"image-20210729125943211\"

\n

pom.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.ajream</groupId>
<artifactId>hello_spring</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

</project>
\n

代码

    \n
  1. 在java文件夹下创建com.xxx.pojo.Hello
  2. \n
\n

\"image-20210729130640647\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.ajream.pojo;

public class Hello {
private String str1; //先不管属性名为什么是str1

public void setStr2(String s){ //先不管为什么函数名设置为setStr2
this.str1 = s;
}

public void printStr(){
System.out.println("Hello:->" + str1);
}
}

\n
    \n
  1. 在resources下创建beans.xml配置文件
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="helloworld" class="com.ajream.pojo.Hello">
<!-- 通过name来识别出调用了哪个函数, value为传入参数-->
<property name="str2" value="spring" /> <!--这行代码表示调用函数 setStr2, 传入参数为字符串"spring" -->

</bean>
</beans>
\n
\n

该文件内容模板可以从spring官网中找到

\n

\"image-20210729131316578\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions go here -->

</beans>
\n
\n
    \n
  1. 在test/java下创建MyTest类来进行测试输出:
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import com.ajream.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //加载xml配置文件
Hello h = (Hello) context.getBean("helloworld");
h.printStr();
}
}

\n

输出:

\n

\"image-20210729131704285\"

\n

三者之间的关系

\"image-20210729134624753\"

\n

执行流程:

\n
    \n
  1. 获取 beans.xml 配置文件信息
  2. \n
  3. 根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法
  4. \n
  5. 调用函数printStr
  6. \n
\n"},{"title":"Spring(5)-ioc创建对象方式","abbrlink":"d8642fb9","date":"2021-07-26T06:58:44.000Z","description":"spring如何使用配置文件来创建对象、注入属性","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n# ioc创建对象方式\n\n\n\n## 构造函数注入\n\nUser.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public User(String name){ //构造函数\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n\n```\n\n\n\n1. 下标指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n2. 变量名指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n3. 类型指定参数(不建议,有可能多个参数的类型相同)\n\n ```xml\n \n \n \n ```\n\n4. 构造函数没有参数的话\n\n ```xml\n \n \n ```\n\n\n\n## set注入\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public void setName(String name){ // set注入\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n```\n\n\n\n```xml\n\n \n\n```\n\n","source":"_posts/Spring/5-ioc创建对象方式.md","raw":"---\ntitle: Spring(5)-ioc创建对象方式\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: d8642fb9\ndate: 2021-07-26 14:58:44\ndescription: spring如何使用配置文件来创建对象、注入属性\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n# ioc创建对象方式\n\n\n\n## 构造函数注入\n\nUser.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public User(String name){ //构造函数\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n\n```\n\n\n\n1. 下标指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n2. 变量名指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n3. 类型指定参数(不建议,有可能多个参数的类型相同)\n\n ```xml\n \n \n \n ```\n\n4. 构造函数没有参数的话\n\n ```xml\n \n \n ```\n\n\n\n## set注入\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public void setName(String name){ // set注入\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n```\n\n\n\n```xml\n\n \n\n```\n\n","slug":"Spring/5-ioc创建对象方式","published":1,"updated":"2021-09-10T03:18:20.605Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pe002takvedtbz9vw9","content":"

ioc创建对象方式

构造函数注入

User.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.pojo;

public class User {
private String name;

public User(String name){ //构造函数
this.name = name;
}

public String getName(){
return name;
}

public void show(){
System.out.println("name: " + name);
}
}

\n
    \n
  1. 下标指定参数

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg index="0" value="张三" /> <!-- 0表示第一个参数-->
    </bean>
    \n
  2. \n
\n
    \n
  1. 变量名指定参数

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg name="name" value="李四"/>
    </bean>
    \n
  2. \n
\n
    \n
  1. 类型指定参数(不建议,有可能多个参数的类型相同)

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg type="String" value="王五" />
    </bean>
    \n
  2. \n
  3. 构造函数没有参数的话

    \n
    1
    2
    <bean id="user" class="com.ajream.pojo.User">
    </bean>
    \n
  4. \n
\n

set注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.pojo;

public class User {
private String name;

public void setName(String name){ // set注入
this.name = name;
}

public String getName(){
return name;
}

public void show(){
System.out.println("name: " + name);
}
}
\n
1
2
3
<bean id="user" class="com.ajream.pojo.User">
<property name="name" value="李四"/>
</bean>
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

ioc创建对象方式

构造函数注入

User.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.pojo;

public class User {
private String name;

public User(String name){ //构造函数
this.name = name;
}

public String getName(){
return name;
}

public void show(){
System.out.println("name: " + name);
}
}

\n
    \n
  1. 下标指定参数

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg index="0" value="张三" /> <!-- 0表示第一个参数-->
    </bean>
    \n
  2. \n
\n
    \n
  1. 变量名指定参数

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg name="name" value="李四"/>
    </bean>
    \n
  2. \n
\n
    \n
  1. 类型指定参数(不建议,有可能多个参数的类型相同)

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg type="String" value="王五" />
    </bean>
    \n
  2. \n
  3. 构造函数没有参数的话

    \n
    1
    2
    <bean id="user" class="com.ajream.pojo.User">
    </bean>
    \n
  4. \n
\n

set注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.pojo;

public class User {
private String name;

public void setName(String name){ // set注入
this.name = name;
}

public String getName(){
return name;
}

public void show(){
System.out.println("name: " + name);
}
}
\n
1
2
3
<bean id="user" class="com.ajream.pojo.User">
<property name="name" value="李四"/>
</bean>
\n"},{"title":"Spring(3)-ioc控制反转","abbrlink":"d959138b","date":"2021-07-25T05:25:37.000Z","description":"spring控制反转(IOC)思想举例理解","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n# IOC控制反转思想举例\n\n\n\n## 创建工程\n\n利用Maven创建一个空白工程spring1,其目录结构如下:\n\n![image-20210728212756154](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-27-57_image-20210728212756154.png)\n\n\n\n然后创建以下几个接口和类:\n\n1. Dao层\n2. Service业务层\n3. 用户层(test表示用户测试)\n\n\n![image-20210728213117047](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-31-18_image-20210728213117047.png)\n\n\n\n\n\n## 代码实现\n\n目标:用户要在test1中创建一个对象,调用Dao层的某个方法 `getUser()`来实现某种功能\n\n\n\n\n\n### Dao层\n\nUserDao接口:\n\n```java\npackage com.ajream.Dao;\n\npublic interface UserDao {\n void getUser();\n}\n```\n\nUserDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserDaoImpl implements UserDao{\n @Override\n public void getUser() {\n System.out.println(\"默认获取用户数据。。。\");\n }\n}\n```\n\nUserMysqlDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserMysqlDaoImpl implements UserDao{\n\n @Override\n public void getUser() {\n System.out.println(\"MySQL获取用户数据。。。\");\n }\n}\n\n```\n\n\n\n\n\n\n\n### 没有使用控制反转时的业务层\n\nUserService接口:\n\n```java\npackage com.ajream.Service;\n\npublic interface UserService {\n void getUser();\n}\n```\n\nUserServiceImpl:\n\n![image-20210728215047346](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-50-48_image-20210728215047346.png)\n\n即:\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\nimport com.ajream.Dao.UserDaoImpl;\n\npublic class UserServiceImpl implements UserService {\n \n private UserDao userDao = new UserDaoImpl(); //注意此处\n\n public UserServiceImpl() {\n }\n\n public void getUser() {\n this.userDao.getUser();\n }\n}\n```\n\n\n\n> 注意没有使用IOC时第8行代码,这样写的缺点:\n>\n> 这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:\n>\n> ```java\n> private UserDao userDao = new UserMysqlDaoImpl();\n> ```\n>\n> 很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现\n\n\n\n\n\n### 使用控制反转后的业务层\n\nUserService接口不变\n\nUserServiceImpl使用setUser来创建对象:\n\n![image-20210728220123436](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-01-24_image-20210728220123436.png)\n\n\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\n\n\npublic class UserServiceImpl implements UserService{\n private UserDao userDao;\n @Override\n public void getUser() {\n userDao.getUser();\n }\n public void setUser(UserDao userDao){\n this.userDao = userDao;\n }\n}\n```\n\n> 用户可以通过调用`setUser()`方法来创建不同对象(只需要传入不同参数即可)\n\n\n\n对比:\n\n![image-20210728221513985](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-15-15_image-20210728221513985.png)\n\n---\n\n\n\ntest1(这里是用户调用业务层实现业务的部分)\n\n- 没有使用IOC时\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n\n userService.getUser();\n }\n}\n\n```\n\n\n\n- 使用了IOC的思想后\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n \n// 通过修改setUser参数来反向创建不同对象\n ((UserServiceImpl) userService).setUser(new UserDaoImpl()); \n userService.getUser();\n \n ((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl());\n userService.getUser();\n }\n}\n\n```\n\n\n\n对比:\n\n![image-20210728221756345](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-17-57_image-20210728221756345.png)\n\n\n\n\n\n## 一张图表示三者之间关系\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n用户希望通过调用业务层来实现各个具体的业务如 `Mysql`、`Oracle`,...\n\n\n\n","source":"_posts/Spring/3-ioc控制反转.md","raw":"---\ntitle: Spring(3)-ioc控制反转\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: d959138b\ndate: 2021-07-25 13:25:37\ndescription: spring控制反转(IOC)思想举例理解\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n# IOC控制反转思想举例\n\n\n\n## 创建工程\n\n利用Maven创建一个空白工程spring1,其目录结构如下:\n\n![image-20210728212756154](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-27-57_image-20210728212756154.png)\n\n\n\n然后创建以下几个接口和类:\n\n1. Dao层\n2. Service业务层\n3. 用户层(test表示用户测试)\n\n\n![image-20210728213117047](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-31-18_image-20210728213117047.png)\n\n\n\n\n\n## 代码实现\n\n目标:用户要在test1中创建一个对象,调用Dao层的某个方法 `getUser()`来实现某种功能\n\n\n\n\n\n### Dao层\n\nUserDao接口:\n\n```java\npackage com.ajream.Dao;\n\npublic interface UserDao {\n void getUser();\n}\n```\n\nUserDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserDaoImpl implements UserDao{\n @Override\n public void getUser() {\n System.out.println(\"默认获取用户数据。。。\");\n }\n}\n```\n\nUserMysqlDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserMysqlDaoImpl implements UserDao{\n\n @Override\n public void getUser() {\n System.out.println(\"MySQL获取用户数据。。。\");\n }\n}\n\n```\n\n\n\n\n\n\n\n### 没有使用控制反转时的业务层\n\nUserService接口:\n\n```java\npackage com.ajream.Service;\n\npublic interface UserService {\n void getUser();\n}\n```\n\nUserServiceImpl:\n\n![image-20210728215047346](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-50-48_image-20210728215047346.png)\n\n即:\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\nimport com.ajream.Dao.UserDaoImpl;\n\npublic class UserServiceImpl implements UserService {\n \n private UserDao userDao = new UserDaoImpl(); //注意此处\n\n public UserServiceImpl() {\n }\n\n public void getUser() {\n this.userDao.getUser();\n }\n}\n```\n\n\n\n> 注意没有使用IOC时第8行代码,这样写的缺点:\n>\n> 这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:\n>\n> ```java\n> private UserDao userDao = new UserMysqlDaoImpl();\n> ```\n>\n> 很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现\n\n\n\n\n\n### 使用控制反转后的业务层\n\nUserService接口不变\n\nUserServiceImpl使用setUser来创建对象:\n\n![image-20210728220123436](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-01-24_image-20210728220123436.png)\n\n\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\n\n\npublic class UserServiceImpl implements UserService{\n private UserDao userDao;\n @Override\n public void getUser() {\n userDao.getUser();\n }\n public void setUser(UserDao userDao){\n this.userDao = userDao;\n }\n}\n```\n\n> 用户可以通过调用`setUser()`方法来创建不同对象(只需要传入不同参数即可)\n\n\n\n对比:\n\n![image-20210728221513985](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-15-15_image-20210728221513985.png)\n\n---\n\n\n\ntest1(这里是用户调用业务层实现业务的部分)\n\n- 没有使用IOC时\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n\n userService.getUser();\n }\n}\n\n```\n\n\n\n- 使用了IOC的思想后\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n \n// 通过修改setUser参数来反向创建不同对象\n ((UserServiceImpl) userService).setUser(new UserDaoImpl()); \n userService.getUser();\n \n ((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl());\n userService.getUser();\n }\n}\n\n```\n\n\n\n对比:\n\n![image-20210728221756345](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-17-57_image-20210728221756345.png)\n\n\n\n\n\n## 一张图表示三者之间关系\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n用户希望通过调用业务层来实现各个具体的业务如 `Mysql`、`Oracle`,...\n\n\n\n","slug":"Spring/3-ioc控制反转","published":1,"updated":"2021-09-10T03:17:53.109Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pf002xakve102r3c73","content":"

IOC控制反转思想举例

创建工程

利用Maven创建一个空白工程spring1,其目录结构如下:

\n

\"image-20210728212756154\"

\n

然后创建以下几个接口和类:

\n
    \n
  1. Dao层
  2. \n
  3. Service业务层
  4. \n
  5. 用户层(test表示用户测试)
  6. \n
\n

\"image-20210728213117047\"

\n

代码实现

目标:用户要在test1中创建一个对象,调用Dao层的某个方法 getUser()来实现某种功能

\n

Dao层

UserDao接口:

\n
1
2
3
4
5
package com.ajream.Dao;

public interface UserDao {
void getUser();
}
\n

UserDaoImpl:

\n
1
2
3
4
5
6
7
8
package com.ajream.Dao;

public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("默认获取用户数据。。。");
}
}
\n

UserMysqlDaoImpl:

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.Dao;

public class UserMysqlDaoImpl implements UserDao{

@Override
public void getUser() {
System.out.println("MySQL获取用户数据。。。");
}
}

\n

没有使用控制反转时的业务层

UserService接口:

\n
1
2
3
4
5
package com.ajream.Service;

public interface UserService {
void getUser();
}
\n

UserServiceImpl:

\n

\"image-20210728215047346\"

\n

即:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.Service;

import com.ajream.Dao.UserDao;
import com.ajream.Dao.UserDaoImpl;

public class UserServiceImpl implements UserService {

private UserDao userDao = new UserDaoImpl(); //注意此处

public UserServiceImpl() {
}

public void getUser() {
this.userDao.getUser();
}
}
\n
\n

注意没有使用IOC时第8行代码,这样写的缺点:

\n

这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:

\n
1
private UserDao userDao = new UserMysqlDaoImpl();
\n

很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现

\n
\n

使用控制反转后的业务层

UserService接口不变

\n

UserServiceImpl使用setUser来创建对象:

\n

\"image-20210728220123436\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.Service;

import com.ajream.Dao.UserDao;


public class UserServiceImpl implements UserService{
private UserDao userDao;
@Override
public void getUser() {
userDao.getUser();
}
public void setUser(UserDao userDao){
this.userDao = userDao;
}
}
\n
\n

用户可以通过调用setUser()方法来创建不同对象(只需要传入不同参数即可)

\n
\n

对比:

\n

\"image-20210728221513985\"

\n
\n

test1(这里是用户调用业务层实现业务的部分)

\n
    \n
  • 没有使用IOC时
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream;

import com.ajream.Dao.UserDaoImpl;
import com.ajream.Dao.UserMysqlDaoImpl;
import com.ajream.Service.UserService;
import com.ajream.Service.UserServiceImpl;

public class test1 {
public static void main(String[] args) {

UserService userService = new UserServiceImpl();

userService.getUser();
}
}

\n
    \n
  • 使用了IOC的思想后
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ajream;

import com.ajream.Dao.UserDaoImpl;
import com.ajream.Dao.UserMysqlDaoImpl;
import com.ajream.Service.UserService;
import com.ajream.Service.UserServiceImpl;

public class test1 {
public static void main(String[] args) {

UserService userService = new UserServiceImpl();

// 通过修改setUser参数来反向创建不同对象
((UserServiceImpl) userService).setUser(new UserDaoImpl());
userService.getUser();

((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl());
userService.getUser();
}
}

\n

对比:

\n

\"image-20210728221756345\"

\n

一张图表示三者之间关系

\"ioc\"

\n

用户希望通过调用业务层来实现各个具体的业务如 MysqlOracle,…

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

IOC控制反转思想举例

创建工程

利用Maven创建一个空白工程spring1,其目录结构如下:

\n

\"image-20210728212756154\"

\n

然后创建以下几个接口和类:

\n
    \n
  1. Dao层
  2. \n
  3. Service业务层
  4. \n
  5. 用户层(test表示用户测试)
  6. \n
\n

\"image-20210728213117047\"

\n

代码实现

目标:用户要在test1中创建一个对象,调用Dao层的某个方法 getUser()来实现某种功能

\n

Dao层

UserDao接口:

\n
1
2
3
4
5
package com.ajream.Dao;

public interface UserDao {
void getUser();
}
\n

UserDaoImpl:

\n
1
2
3
4
5
6
7
8
package com.ajream.Dao;

public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("默认获取用户数据。。。");
}
}
\n

UserMysqlDaoImpl:

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.Dao;

public class UserMysqlDaoImpl implements UserDao{

@Override
public void getUser() {
System.out.println("MySQL获取用户数据。。。");
}
}

\n

没有使用控制反转时的业务层

UserService接口:

\n
1
2
3
4
5
package com.ajream.Service;

public interface UserService {
void getUser();
}
\n

UserServiceImpl:

\n

\"image-20210728215047346\"

\n

即:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.Service;

import com.ajream.Dao.UserDao;
import com.ajream.Dao.UserDaoImpl;

public class UserServiceImpl implements UserService {

private UserDao userDao = new UserDaoImpl(); //注意此处

public UserServiceImpl() {
}

public void getUser() {
this.userDao.getUser();
}
}
\n
\n

注意没有使用IOC时第8行代码,这样写的缺点:

\n

这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:

\n
1
private UserDao userDao = new UserMysqlDaoImpl();
\n

很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现

\n
\n

使用控制反转后的业务层

UserService接口不变

\n

UserServiceImpl使用setUser来创建对象:

\n

\"image-20210728220123436\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.Service;

import com.ajream.Dao.UserDao;


public class UserServiceImpl implements UserService{
private UserDao userDao;
@Override
public void getUser() {
userDao.getUser();
}
public void setUser(UserDao userDao){
this.userDao = userDao;
}
}
\n
\n

用户可以通过调用setUser()方法来创建不同对象(只需要传入不同参数即可)

\n
\n

对比:

\n

\"image-20210728221513985\"

\n
\n

test1(这里是用户调用业务层实现业务的部分)

\n
    \n
  • 没有使用IOC时
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream;

import com.ajream.Dao.UserDaoImpl;
import com.ajream.Dao.UserMysqlDaoImpl;
import com.ajream.Service.UserService;
import com.ajream.Service.UserServiceImpl;

public class test1 {
public static void main(String[] args) {

UserService userService = new UserServiceImpl();

userService.getUser();
}
}

\n
    \n
  • 使用了IOC的思想后
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ajream;

import com.ajream.Dao.UserDaoImpl;
import com.ajream.Dao.UserMysqlDaoImpl;
import com.ajream.Service.UserService;
import com.ajream.Service.UserServiceImpl;

public class test1 {
public static void main(String[] args) {

UserService userService = new UserServiceImpl();

// 通过修改setUser参数来反向创建不同对象
((UserServiceImpl) userService).setUser(new UserDaoImpl());
userService.getUser();

((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl());
userService.getUser();
}
}

\n

对比:

\n

\"image-20210728221756345\"

\n

一张图表示三者之间关系

\"ioc\"

\n

用户希望通过调用业务层来实现各个具体的业务如 MysqlOracle,…

\n"},{"title":"Spring(7)-spring容器注入","abbrlink":"7f2244ba","date":"2021-07-27T06:39:25.000Z","description":"spring容器类型如何注入","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n\n# spring容器注入\n\n [跳转到项目地址](https://codechina.csdn.net/m0_46079750/spring4/-/tree/master)\n\n\n\n如果是`String` 这种简单类型的数据,spring注入如下:\n\n\n\nUser.java\n\n```java\npublic class User{\n private String name;\n \n public void setName(String name){\n this.name = name;\n }\n \n //...\n}\n```\n\nbeans.xml\n\n```xml\n\n\n\t\n\n\n```\n\n\n\n但是,如果要注入的不是如`String` 这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:\n\n[官方文档说明](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/core.html#beans-inner-beans)\n\n```xml\n\n \n \n \n 《abc》\n 《defg》\n 《hijk》\n \n \n \n \n \n \n administrator@example.org\n support@example.org\n development@example.org\n \n \n \n \n \n \n a list element followed by a reference\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n just some string\n \n \n \n\n```\n\n\n\n例如要往下面这些属性注入值\n\n![image-20210730172726839](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-27-28_image-20210730172726839.png)\n\nbeans.xml:\n\n```xml\n\n\n\n \n \n \n\n \n \n\n \n \n\n\n \n\n\n \n \n 《计算机技术》\n 《操作系统》\n 《数据结构》\n \n \n\n\n \n \n 王者荣耀\n 少年三国志\n \n \n\n\n \n \n 打游戏\n 打羽毛球\n \n \n\n\n \n \n \n \n \n \n \n\n\n \n \n administrator@example.org\n 13389453895\n 456382987\n \n \n\n \n\n\n```\n\n\n\n\n\n输出预览:\n\n![image-20210730173016148](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-30-17_image-20210730173016148.png)\n\n","source":"_posts/Spring/7-spring容器注入.md","raw":"---\ntitle: Spring(7)-spring容器注入\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 7f2244ba\ndate: 2021-07-27 14:39:25\ndescription: spring容器类型如何注入\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n\n# spring容器注入\n\n [跳转到项目地址](https://codechina.csdn.net/m0_46079750/spring4/-/tree/master)\n\n\n\n如果是`String` 这种简单类型的数据,spring注入如下:\n\n\n\nUser.java\n\n```java\npublic class User{\n private String name;\n \n public void setName(String name){\n this.name = name;\n }\n \n //...\n}\n```\n\nbeans.xml\n\n```xml\n\n\n\t\n\n\n```\n\n\n\n但是,如果要注入的不是如`String` 这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:\n\n[官方文档说明](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/core.html#beans-inner-beans)\n\n```xml\n\n \n \n \n 《abc》\n 《defg》\n 《hijk》\n \n \n \n \n \n \n administrator@example.org\n support@example.org\n development@example.org\n \n \n \n \n \n \n a list element followed by a reference\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n just some string\n \n \n \n\n```\n\n\n\n例如要往下面这些属性注入值\n\n![image-20210730172726839](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-27-28_image-20210730172726839.png)\n\nbeans.xml:\n\n```xml\n\n\n\n \n \n \n\n \n \n\n \n \n\n\n \n\n\n \n \n 《计算机技术》\n 《操作系统》\n 《数据结构》\n \n \n\n\n \n \n 王者荣耀\n 少年三国志\n \n \n\n\n \n \n 打游戏\n 打羽毛球\n \n \n\n\n \n \n \n \n \n \n \n\n\n \n \n administrator@example.org\n 13389453895\n 456382987\n \n \n\n \n\n\n```\n\n\n\n\n\n输出预览:\n\n![image-20210730173016148](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-30-17_image-20210730173016148.png)\n\n","slug":"Spring/7-spring容器注入","published":1,"updated":"2021-09-10T03:18:32.070Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pg002zakve9wvvbwrm","content":"

spring容器注入

跳转到项目地址

\n

如果是String 这种简单类型的数据,spring注入如下:

\n

User.java

\n
1
2
3
4
5
6
7
8
9
public class User{
private String name;

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

//...
}
\n

beans.xml

\n
1
2
3
4
5
<!--前面的省略-->
<bean id="user" class="com.xxx.User">
\t<property name="name" value="张三" />
</bean>

\n

但是,如果要注入的不是如String 这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:

\n

官方文档说明

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- array注入 -->
<property name="books">
<array>
<value>《abc》</value>
<value>《defg》</value>
<value>《hijk》</value>
</array>
</property>

<!-- prop注入 -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>

<!-- list注入 -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>

<!-- map -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>

<!-- set注入 -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
\n

例如要往下面这些属性注入值

\n

\"image-20210730172726839\"

\n

beans.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="address" class="com.ajream.pojo.Address" >
<property name="addr" value="西安"/>
</bean>

<bean id="student" class="com.ajream.pojo.Student">

<!-- 普通值注入-->
<property name="name" value="王二"/>
<property name="age" value="30"/>

<!-- bean注入-->
<property name="address" ref="address" />

<!-- 数组array注入-->
<property name="books">
<array>
<value>《计算机技术》</value>
<value>《操作系统》</value>
<value>《数据结构》</value>
</array>
</property>

<!-- set注入-->
<property name="games">
<set>
<value>王者荣耀</value>
<value>少年三国志</value>
</set>
</property>

<!-- list注入-->
<property name="hobbies">
<list>
<value>打游戏</value>
<value>打羽毛球</value>
</list>
</property>

<!-- map注入-->
<property name="grade">
<map>
<entry key="高等数学" value="85"/>
<entry key="大学英语" value="90"/>
<entry key="java基础" value="98"/>
</map>
</property>

<!-- prop注入,类似map-->
<property name="info">
<props>
<prop key="email">administrator@example.org</prop>
<prop key="phone">13389453895</prop>
<prop key="QQ-number">456382987</prop>
</props>
</property>

</bean>

</beans>
\n

输出预览:

\n

\"image-20210730173016148\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

spring容器注入

跳转到项目地址

\n

如果是String 这种简单类型的数据,spring注入如下:

\n

User.java

\n
1
2
3
4
5
6
7
8
9
public class User{
private String name;

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

//...
}
\n

beans.xml

\n
1
2
3
4
5
<!--前面的省略-->
<bean id="user" class="com.xxx.User">
\t<property name="name" value="张三" />
</bean>

\n

但是,如果要注入的不是如String 这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:

\n

官方文档说明

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- array注入 -->
<property name="books">
<array>
<value>《abc》</value>
<value>《defg》</value>
<value>《hijk》</value>
</array>
</property>

<!-- prop注入 -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>

<!-- list注入 -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>

<!-- map -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>

<!-- set注入 -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
\n

例如要往下面这些属性注入值

\n

\"image-20210730172726839\"

\n

beans.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="address" class="com.ajream.pojo.Address" >
<property name="addr" value="西安"/>
</bean>

<bean id="student" class="com.ajream.pojo.Student">

<!-- 普通值注入-->
<property name="name" value="王二"/>
<property name="age" value="30"/>

<!-- bean注入-->
<property name="address" ref="address" />

<!-- 数组array注入-->
<property name="books">
<array>
<value>《计算机技术》</value>
<value>《操作系统》</value>
<value>《数据结构》</value>
</array>
</property>

<!-- set注入-->
<property name="games">
<set>
<value>王者荣耀</value>
<value>少年三国志</value>
</set>
</property>

<!-- list注入-->
<property name="hobbies">
<list>
<value>打游戏</value>
<value>打羽毛球</value>
</list>
</property>

<!-- map注入-->
<property name="grade">
<map>
<entry key="高等数学" value="85"/>
<entry key="大学英语" value="90"/>
<entry key="java基础" value="98"/>
</map>
</property>

<!-- prop注入,类似map-->
<property name="info">
<props>
<prop key="email">administrator@example.org</prop>
<prop key="phone">13389453895</prop>
<prop key="QQ-number">456382987</prop>
</props>
</property>

</bean>

</beans>
\n

输出预览:

\n

\"image-20210730173016148\"

\n"},{"title":"Spring(6)-spring配置说明","abbrlink":"3fb3b16d","date":"2021-07-26T05:26:19.000Z","description":"spring一些基本配置","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n\n# spring配置\n\n\n\n## 别名alias\n\nbean可以拥有别名\n\n```xml\n\n```\n\n\n\n例如:\n\n```xml\n\n \n \n\n\n \n```\n\n\n\n使用别名后就可以通过别名来获取bean\n\n```java\n//User user = context.getBean(\"user\", User.class);\nUser user = context.getBean(\"user2\", User.class);\n```\n\n\n\n## bean的配置\n\n```xml\n\n\n \n\n```\n\n\n\n## import\n\n如果有多个 `beans`配置文件,可以用 `import`将多个文件导入到一个文件中\n\n例如几个配置文件如下:\n\n```\nresources\n\t|-applicationcontext.xml\n\t|-beans1.xml\n\t|-beans2.xml\n```\n\n在 `applicationcontext.xml` 中可以使用 `import` 标签导入 另外2个配置文件:\n\n```xml\n\n\n```\n\n> 适用于团队合作时使用\n\n\n\n","source":"_posts/Spring/6-spring配置说明.md","raw":"---\ntitle: Spring(6)-spring配置说明\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 3fb3b16d\ndate: 2021-07-26 13:26:19\ndescription: spring一些基本配置\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n\n# spring配置\n\n\n\n## 别名alias\n\nbean可以拥有别名\n\n```xml\n\n```\n\n\n\n例如:\n\n```xml\n\n \n \n\n\n \n```\n\n\n\n使用别名后就可以通过别名来获取bean\n\n```java\n//User user = context.getBean(\"user\", User.class);\nUser user = context.getBean(\"user2\", User.class);\n```\n\n\n\n## bean的配置\n\n```xml\n\n\n \n\n```\n\n\n\n## import\n\n如果有多个 `beans`配置文件,可以用 `import`将多个文件导入到一个文件中\n\n例如几个配置文件如下:\n\n```\nresources\n\t|-applicationcontext.xml\n\t|-beans1.xml\n\t|-beans2.xml\n```\n\n在 `applicationcontext.xml` 中可以使用 `import` 标签导入 另外2个配置文件:\n\n```xml\n\n\n```\n\n> 适用于团队合作时使用\n\n\n\n","slug":"Spring/6-spring配置说明","published":1,"updated":"2021-09-10T03:18:24.710Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pi0032akve2d4z141s","content":"

spring配置

别名alias

bean可以拥有别名

\n
1
<alias name="fromName" alias="toName"/>
\n

例如:

\n
1
2
3
4
5
6
<bean id="user" class="com.ajream.pojo.User">
<property name="name" value="章三" />
<property name="age" value="20" />
</bean>

<alias name="user" alias="user2"/>
\n

使用别名后就可以通过别名来获取bean

\n
1
2
//User user = context.getBean("user", User.class);
User user = context.getBean("user2", User.class);
\n

bean的配置

1
2
3
4
5
6
7
8
<!--
id: bean的唯一标识符,相当于创建对象`User user = new User()`的对象名user
class:指向的类
name:也是别名,并且可以取多个,中间可以用逗号、分号、空格隔开
-->
<bean id="user" class="com.ajream.pojo.User" name="n1,n2;n3 n4">
<property name="name" value="章三" />
</bean>
\n

import

如果有多个 beans配置文件,可以用 import将多个文件导入到一个文件中

\n

例如几个配置文件如下:

\n
1
2
3
4
resources
\t|-applicationcontext.xml
\t|-beans1.xml
\t|-beans2.xml
\n

applicationcontext.xml 中可以使用 import 标签导入 另外2个配置文件:

\n
1
2
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
\n
\n

适用于团队合作时使用

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

spring配置

别名alias

bean可以拥有别名

\n
1
<alias name="fromName" alias="toName"/>
\n

例如:

\n
1
2
3
4
5
6
<bean id="user" class="com.ajream.pojo.User">
<property name="name" value="章三" />
<property name="age" value="20" />
</bean>

<alias name="user" alias="user2"/>
\n

使用别名后就可以通过别名来获取bean

\n
1
2
//User user = context.getBean("user", User.class);
User user = context.getBean("user2", User.class);
\n

bean的配置

1
2
3
4
5
6
7
8
<!--
id: bean的唯一标识符,相当于创建对象`User user = new User()`的对象名user
class:指向的类
name:也是别名,并且可以取多个,中间可以用逗号、分号、空格隔开
-->
<bean id="user" class="com.ajream.pojo.User" name="n1,n2;n3 n4">
<property name="name" value="章三" />
</bean>
\n

import

如果有多个 beans配置文件,可以用 import将多个文件导入到一个文件中

\n

例如几个配置文件如下:

\n
1
2
3
4
resources
\t|-applicationcontext.xml
\t|-beans1.xml
\t|-beans2.xml
\n

applicationcontext.xml 中可以使用 import 标签导入 另外2个配置文件:

\n
1
2
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
\n
\n

适用于团队合作时使用

\n
\n"},{"title":"Spring(9)-bean自动装配","abbrlink":"f68104ec","date":"2021-07-28T09:26:49.000Z","description":"spring自动装配(自动根据一些配置说明来注入属性)的使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n# bean自动装配\n\n\n\n[![spring5](https://codechina.csdn.net/m0_46079750/spring5/-/card.svg)](https://codechina.csdn.net/m0_46079750/spring5)\n\n## 代码\n\n用一个项目来说明,如下:\n\n![image-20210731164147058](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-41-53_image-20210731164147058.png)\n\n其中各个文件代码如下:\n\n\n\nCat:\n\n```java\npackage com.ajream.pojo;\n\npublic class Cat {\n public void shout(){\n System.out.println(\"miao~\");\n }\n}\n```\n\n\n\nDog:\n\n```java\npackage com.ajream.pojo;\n\npublic class Dog {\n public void shout(){\n System.out.println(\"wang~\");\n }\n}\n\n```\n\n\n\nPerson:\n\n```java\npackage com.ajream.pojo;\n\npublic class Person {\n private String name;\n private Cat cat;\n private Dog dog;\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public Cat getCat() {\n return cat;\n }\n\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n public Dog getDog() {\n return dog;\n }\n\n public void setDog(Dog dog) {\n this.dog = dog;\n }\n\n @Override\n public String toString() {\n return \"Person{\" +\n \"name='\" + name + '\\'' +\n \", cat=\" + cat +\n \", dog=\" + dog +\n '}';\n }\n\n}\n\n```\n\nbeans.xml\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n```\n\n\n\nMyTest:\n\n```java\nimport com.ajream.pojo.Person;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\n\npublic class MyTest {\n\n public static void main(String[] args) {\n \n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\");\n Person p = context.getBean(\"person\", Person.class);\n\n System.out.println(p.getName());\n p.getCat().shout();\n p.getDog().shout();\n\n }\n\n}\n\n```\n\n\n\n## 分析\n\n主要看 `Person`类和 `beans.xml`文件,在没有使用自动装配时\n\n![image-20210731165352656](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-53-57_image-20210731165352656.png)\n\n\n\n1. 使用 `byName` 自动装配\n\n![image-20210731170102634](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-01-06_image-20210731170102634.png)\n\n\n\n使用自动装配就添加了 `autowire = \"byName\"` 这一配置,要求如下:\n\n![image-20210731170646544](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-06-49_image-20210731170646544.png)\n\nbyName就相当于`name`和`ref`相同的情形(这也是一般情形),而由于`name`又与 `setName` 关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:\n\n![image-20210731171212541](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-12-15_image-20210731171212541.png)\n\n\n\n\n\n2. 使用 `byType` 自动装配\n\n\n\n对id名无要求,但要求bean类型唯一\n\n![image-20210731171743101](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-17-46_image-20210731171743101.png)\n\n比如下面这种情况就会出错:\n\n![image-20210731172014553](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-20-19_image-20210731172014553.png)\n\n\n\n> 总结:\n>\n> - 使用byName方式的自动装配,bean的id要与set方法保持一致\n> - 使用byType方式的自动装配,bean的类型要唯一,否则就会出错\n\n","source":"_posts/Spring/9-bean自动装配.md","raw":"---\ntitle: Spring(9)-bean自动装配\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: f68104ec\ndate: 2021-07-28 17:26:49\ndescription: spring自动装配(自动根据一些配置说明来注入属性)的使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n# bean自动装配\n\n\n\n[![spring5](https://codechina.csdn.net/m0_46079750/spring5/-/card.svg)](https://codechina.csdn.net/m0_46079750/spring5)\n\n## 代码\n\n用一个项目来说明,如下:\n\n![image-20210731164147058](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-41-53_image-20210731164147058.png)\n\n其中各个文件代码如下:\n\n\n\nCat:\n\n```java\npackage com.ajream.pojo;\n\npublic class Cat {\n public void shout(){\n System.out.println(\"miao~\");\n }\n}\n```\n\n\n\nDog:\n\n```java\npackage com.ajream.pojo;\n\npublic class Dog {\n public void shout(){\n System.out.println(\"wang~\");\n }\n}\n\n```\n\n\n\nPerson:\n\n```java\npackage com.ajream.pojo;\n\npublic class Person {\n private String name;\n private Cat cat;\n private Dog dog;\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public Cat getCat() {\n return cat;\n }\n\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n public Dog getDog() {\n return dog;\n }\n\n public void setDog(Dog dog) {\n this.dog = dog;\n }\n\n @Override\n public String toString() {\n return \"Person{\" +\n \"name='\" + name + '\\'' +\n \", cat=\" + cat +\n \", dog=\" + dog +\n '}';\n }\n\n}\n\n```\n\nbeans.xml\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n```\n\n\n\nMyTest:\n\n```java\nimport com.ajream.pojo.Person;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\n\npublic class MyTest {\n\n public static void main(String[] args) {\n \n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\");\n Person p = context.getBean(\"person\", Person.class);\n\n System.out.println(p.getName());\n p.getCat().shout();\n p.getDog().shout();\n\n }\n\n}\n\n```\n\n\n\n## 分析\n\n主要看 `Person`类和 `beans.xml`文件,在没有使用自动装配时\n\n![image-20210731165352656](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-53-57_image-20210731165352656.png)\n\n\n\n1. 使用 `byName` 自动装配\n\n![image-20210731170102634](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-01-06_image-20210731170102634.png)\n\n\n\n使用自动装配就添加了 `autowire = \"byName\"` 这一配置,要求如下:\n\n![image-20210731170646544](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-06-49_image-20210731170646544.png)\n\nbyName就相当于`name`和`ref`相同的情形(这也是一般情形),而由于`name`又与 `setName` 关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:\n\n![image-20210731171212541](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-12-15_image-20210731171212541.png)\n\n\n\n\n\n2. 使用 `byType` 自动装配\n\n\n\n对id名无要求,但要求bean类型唯一\n\n![image-20210731171743101](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-17-46_image-20210731171743101.png)\n\n比如下面这种情况就会出错:\n\n![image-20210731172014553](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-20-19_image-20210731172014553.png)\n\n\n\n> 总结:\n>\n> - 使用byName方式的自动装配,bean的id要与set方法保持一致\n> - 使用byType方式的自动装配,bean的类型要唯一,否则就会出错\n\n","slug":"Spring/9-bean自动装配","published":1,"updated":"2021-09-10T03:17:09.012Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pj0035akvehrre9dbw","content":"

bean自动装配

\"spring5\"

\n

代码

用一个项目来说明,如下:

\n

\"image-20210731164147058\"

\n

其中各个文件代码如下:

\n

Cat:

\n
1
2
3
4
5
6
7
package com.ajream.pojo;

public class Cat {
public void shout(){
System.out.println("miao~");
}
}
\n

Dog:

\n
1
2
3
4
5
6
7
8
package com.ajream.pojo;

public class Dog {
public void shout(){
System.out.println("wang~");
}
}

\n

Person:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.ajream.pojo;

public class Person {
private String name;
private Cat cat;
private Dog dog;

public String getName() {
return name;
}

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

public Cat getCat() {
return cat;
}

public void setCat(Cat cat) {
this.cat = cat;
}

public Dog getDog() {
return dog;
}

public void setDog(Dog dog) {
this.dog = dog;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}

}

\n

beans.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="dog" class="com.ajream.pojo.Dog"/>
<bean id="cat" class="com.ajream.pojo.Cat"/>
<bean id="person" class="com.ajream.pojo.Person">
<property name="name" value="张三"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
\n

MyTest:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.ajream.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class MyTest {

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person p = context.getBean("person", Person.class);

System.out.println(p.getName());
p.getCat().shout();
p.getDog().shout();

}

}

\n

分析

主要看 Person类和 beans.xml文件,在没有使用自动装配时

\n

\"image-20210731165352656\"

\n
    \n
  1. 使用 byName 自动装配
  2. \n
\n

\"image-20210731170102634\"

\n

使用自动装配就添加了 autowire = "byName" 这一配置,要求如下:

\n

\"image-20210731170646544\"

\n

byName就相当于nameref相同的情形(这也是一般情形),而由于name又与 setName 关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:

\n

\"image-20210731171212541\"

\n
    \n
  1. 使用 byType 自动装配
  2. \n
\n

对id名无要求,但要求bean类型唯一

\n

\"image-20210731171743101\"

\n

比如下面这种情况就会出错:

\n

\"image-20210731172014553\"

\n
\n

总结:

\n
    \n
  • 使用byName方式的自动装配,bean的id要与set方法保持一致
  • \n
  • 使用byType方式的自动装配,bean的类型要唯一,否则就会出错
  • \n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

bean自动装配

\"spring5\"

\n

代码

用一个项目来说明,如下:

\n

\"image-20210731164147058\"

\n

其中各个文件代码如下:

\n

Cat:

\n
1
2
3
4
5
6
7
package com.ajream.pojo;

public class Cat {
public void shout(){
System.out.println("miao~");
}
}
\n

Dog:

\n
1
2
3
4
5
6
7
8
package com.ajream.pojo;

public class Dog {
public void shout(){
System.out.println("wang~");
}
}

\n

Person:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.ajream.pojo;

public class Person {
private String name;
private Cat cat;
private Dog dog;

public String getName() {
return name;
}

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

public Cat getCat() {
return cat;
}

public void setCat(Cat cat) {
this.cat = cat;
}

public Dog getDog() {
return dog;
}

public void setDog(Dog dog) {
this.dog = dog;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}

}

\n

beans.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="dog" class="com.ajream.pojo.Dog"/>
<bean id="cat" class="com.ajream.pojo.Cat"/>
<bean id="person" class="com.ajream.pojo.Person">
<property name="name" value="张三"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
\n

MyTest:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.ajream.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class MyTest {

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person p = context.getBean("person", Person.class);

System.out.println(p.getName());
p.getCat().shout();
p.getDog().shout();

}

}

\n

分析

主要看 Person类和 beans.xml文件,在没有使用自动装配时

\n

\"image-20210731165352656\"

\n
    \n
  1. 使用 byName 自动装配
  2. \n
\n

\"image-20210731170102634\"

\n

使用自动装配就添加了 autowire = "byName" 这一配置,要求如下:

\n

\"image-20210731170646544\"

\n

byName就相当于nameref相同的情形(这也是一般情形),而由于name又与 setName 关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:

\n

\"image-20210731171212541\"

\n
    \n
  1. 使用 byType 自动装配
  2. \n
\n

对id名无要求,但要求bean类型唯一

\n

\"image-20210731171743101\"

\n

比如下面这种情况就会出错:

\n

\"image-20210731172014553\"

\n
\n

总结:

\n
    \n
  • 使用byName方式的自动装配,bean的id要与set方法保持一致
  • \n
  • 使用byType方式的自动装配,bean的类型要唯一,否则就会出错
  • \n
\n
\n"},{"title":"Spring(8)-命名空间和作用域","abbrlink":"3feecb5e","date":"2021-07-27T09:57:24.000Z","description":"spring的p、c命名空间使用,bean的作用域","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 命名空间和bean作用域\n\n\n\n## 命名空间\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master/)\n\n### p命名空间\n\n在beans中添加p命名空间的支持\n\n```xml\nxmlns:p=\"http://www.springframework.org/schema/p\"\n```\n\n\n\n以前要对一个属性进行注入,一般是按这种方式:\n\n```xml\n\n \n\n```\n\n使用了p命名空间,可以这样写:\n\n```xml\n\n```\n\np就是 `property` 的简写\n\n### c命名空间\n\n> c代表了 `constructor-arg`,因此c命名空间是通过构造函数来进行注入的\n\n在beans中添加c命名空间的支持\n\n```xml\nxmlns:c=\"http://www.springframework.org/schema/c\"\n```\n\n未使用命名空间:\n\n```xml\n\n \n\n```\n\n使用c命名空间:\n\n```xml\n\n```\n\n\n\n另外,p命名空间和c命名空间还可以一起使用\n\n\n\n## bean作用域\n\n\n\n### 单例模式\n\n(默认使用单例模式)\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/singleton.png)\n\n说明:\n\n```xml\n\n\n\n\n\n```\n\n用户每次从spring获取的bean,其hashcode是相同的, \n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2的hashcode相同\n```\n\n\n\n### 原生模式\n\n\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/prototype.png\"prototype\")\n\n```xml\n\n```\n\n用户每次从spring获取的bean,其hashcode是不同的\n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2 的 hashcode 不相同\n```\n\n\n\n\n\n\n\n","source":"_posts/Spring/8-命名空间和作用域.md","raw":"---\ntitle: Spring(8)-命名空间和作用域\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 3feecb5e\ndate: 2021-07-27 17:57:24\ndescription: spring的p、c命名空间使用,bean的作用域\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 命名空间和bean作用域\n\n\n\n## 命名空间\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master/)\n\n### p命名空间\n\n在beans中添加p命名空间的支持\n\n```xml\nxmlns:p=\"http://www.springframework.org/schema/p\"\n```\n\n\n\n以前要对一个属性进行注入,一般是按这种方式:\n\n```xml\n\n \n\n```\n\n使用了p命名空间,可以这样写:\n\n```xml\n\n```\n\np就是 `property` 的简写\n\n### c命名空间\n\n> c代表了 `constructor-arg`,因此c命名空间是通过构造函数来进行注入的\n\n在beans中添加c命名空间的支持\n\n```xml\nxmlns:c=\"http://www.springframework.org/schema/c\"\n```\n\n未使用命名空间:\n\n```xml\n\n \n\n```\n\n使用c命名空间:\n\n```xml\n\n```\n\n\n\n另外,p命名空间和c命名空间还可以一起使用\n\n\n\n## bean作用域\n\n\n\n### 单例模式\n\n(默认使用单例模式)\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/singleton.png)\n\n说明:\n\n```xml\n\n\n\n\n\n```\n\n用户每次从spring获取的bean,其hashcode是相同的, \n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2的hashcode相同\n```\n\n\n\n### 原生模式\n\n\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/prototype.png\"prototype\")\n\n```xml\n\n```\n\n用户每次从spring获取的bean,其hashcode是不同的\n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2 的 hashcode 不相同\n```\n\n\n\n\n\n\n\n","slug":"Spring/8-命名空间和作用域","published":1,"updated":"2021-09-10T03:17:13.986Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pk0038akved9jw5nm3","content":"

命名空间和bean作用域

命名空间

项目地址

\n

p命名空间

在beans中添加p命名空间的支持

\n
1
xmlns:p="http://www.springframework.org/schema/p"
\n

以前要对一个属性进行注入,一般是按这种方式:

\n
1
2
3
<bean id="person" class="com.ajream.pojo.Person">
<property name="name" value="张三"/>
</bean>
\n

使用了p命名空间,可以这样写:

\n
1
<bean id="person" class="com.ajream.pojo.Person" p:name="张三" />
\n

p就是 property 的简写

\n

c命名空间

\n

c代表了 constructor-arg,因此c命名空间是通过构造函数来进行注入的

\n
\n

在beans中添加c命名空间的支持

\n
1
xmlns:c="http://www.springframework.org/schema/c"
\n

未使用命名空间:

\n
1
2
3
<bean id="person" class="com.ajream.pojo.Person">
<constructor-arg name="name" value="张三" />
</bean>
\n

使用c命名空间:

\n
1
<bean id="person" class="com.ajream.pojo.Person" c:name="王五" />
\n

另外,p命名空间和c命名空间还可以一起使用

\n

bean作用域

单例模式

(默认使用单例模式)

\n

\"\"

\n

说明:

\n
1
2
3
4
5
<!--下面这两行代码等效,因为默认是单例(singleton)的 -->

<bean id="accountService" class="com.something.DefaultAccountService"/>

<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
\n

用户每次从spring获取的bean,其hashcode是相同的,

\n
1
2
3
4
DefaultAccountService as1 = context.getBean("accountService");
DefaultAccountService as2 = context.getBean("accountService");

// as1与as2的hashcode相同
\n

原生模式

\"\"

\n
1
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
\n

用户每次从spring获取的bean,其hashcode是不同的

\n
1
2
3
4
DefaultAccountService as1 = context.getBean("accountService");
DefaultAccountService as2 = context.getBean("accountService");

// as1与as2 的 hashcode 不相同
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

命名空间和bean作用域

命名空间

项目地址

\n

p命名空间

在beans中添加p命名空间的支持

\n
1
xmlns:p="http://www.springframework.org/schema/p"
\n

以前要对一个属性进行注入,一般是按这种方式:

\n
1
2
3
<bean id="person" class="com.ajream.pojo.Person">
<property name="name" value="张三"/>
</bean>
\n

使用了p命名空间,可以这样写:

\n
1
<bean id="person" class="com.ajream.pojo.Person" p:name="张三" />
\n

p就是 property 的简写

\n

c命名空间

\n

c代表了 constructor-arg,因此c命名空间是通过构造函数来进行注入的

\n
\n

在beans中添加c命名空间的支持

\n
1
xmlns:c="http://www.springframework.org/schema/c"
\n

未使用命名空间:

\n
1
2
3
<bean id="person" class="com.ajream.pojo.Person">
<constructor-arg name="name" value="张三" />
</bean>
\n

使用c命名空间:

\n
1
<bean id="person" class="com.ajream.pojo.Person" c:name="王五" />
\n

另外,p命名空间和c命名空间还可以一起使用

\n

bean作用域

单例模式

(默认使用单例模式)

\n

\"\"

\n

说明:

\n
1
2
3
4
5
<!--下面这两行代码等效,因为默认是单例(singleton)的 -->

<bean id="accountService" class="com.something.DefaultAccountService"/>

<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
\n

用户每次从spring获取的bean,其hashcode是相同的,

\n
1
2
3
4
DefaultAccountService as1 = context.getBean("accountService");
DefaultAccountService as2 = context.getBean("accountService");

// as1与as2的hashcode相同
\n

原生模式

\"\"

\n
1
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
\n

用户每次从spring获取的bean,其hashcode是不同的

\n
1
2
3
4
DefaultAccountService as1 = context.getBean("accountService");
DefaultAccountService as2 = context.getBean("accountService");

// as1与as2 的 hashcode 不相同
\n"},{"title":"mybatis的Mapper详解(三)","description":"Mapper配置","abbrlink":"f218ca42","date":"2021-08-25T11:12:46.000Z","top_img":null,"cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n## mapper.xml\n\n\n\n在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement\n\n\n\n因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据\n\n\n\n### parameterType: 参数数据类型\n\n- 基本数据类型,int, long, ...\n\n ```xml\n \n delete from t_acount where id = #{id}\n \n ```\n\n \n\n- String类型: `java.lang.String`\n\n ```\n \n ```\n\n- 包装类,如:`java.lang.Integer`, `java.lang.Long`...\n\n- 自定义类类型\n\n ```xml\n \n \tinsert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n ```\n\n \n\n\n\n\n\n方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 `param1`, `param2` ...或者 `arg0`, `arg1` ... 【不同mybatis版本写可能不同,要注意param下标是从1开始的】\n\n例如:\n\n这是方法:\n\n```java\npublic Account findByNameAndAge(name, age);\n```\n\n则mapper如下\n\n```xml\n\n\n```\n\n\n\n\n\n\n\n### resultType:返回类型\n\n用法与parameterType相同\n\n【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是`int`】\n\n- 基本数据类型\n- 包装类\n- String类型\n- 自定义类类型\n\n\n\n\n\n\n\n","source":"_posts/Mybatis/mybatis的Mapper详解(三).md","raw":"---\ntitle: mybatis的Mapper详解(三)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: Mapper配置\nabbrlink: f218ca42\ndate: 2021-08-25 19:12:46\ntop_img: \ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n## mapper.xml\n\n\n\n在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement\n\n\n\n因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据\n\n\n\n### parameterType: 参数数据类型\n\n- 基本数据类型,int, long, ...\n\n ```xml\n \n delete from t_acount where id = #{id}\n \n ```\n\n \n\n- String类型: `java.lang.String`\n\n ```\n \n ```\n\n- 包装类,如:`java.lang.Integer`, `java.lang.Long`...\n\n- 自定义类类型\n\n ```xml\n \n \tinsert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n ```\n\n \n\n\n\n\n\n方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 `param1`, `param2` ...或者 `arg0`, `arg1` ... 【不同mybatis版本写可能不同,要注意param下标是从1开始的】\n\n例如:\n\n这是方法:\n\n```java\npublic Account findByNameAndAge(name, age);\n```\n\n则mapper如下\n\n```xml\n\n\n```\n\n\n\n\n\n\n\n### resultType:返回类型\n\n用法与parameterType相同\n\n【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是`int`】\n\n- 基本数据类型\n- 包装类\n- String类型\n- 自定义类类型\n\n\n\n\n\n\n\n","slug":"Mybatis/mybatis的Mapper详解(三)","published":1,"updated":"2021-08-29T14:45:05.560Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pl003bakve3evtamml","content":"

mapper.xml

在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement

\n

因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据

\n

parameterType: 参数数据类型

    \n
  • 基本数据类型,int, long, …

    \n
    1
    2
    3
    <delete id="deleteById" parameterType="int">
    delete from t_acount where id = #{id}
    </delete>
    \n
  • \n
\n
    \n
  • String类型: java.lang.String

    \n
    1
    2
    3
    <select id="findByName" resultType="com.ajream.entity.Account" parameterType="java.lang.String">
    select * from t_acount where username=#{username}
    </select>
    \n
  • \n
  • 包装类,如:java.lang.Integerjava.lang.Long

    \n
  • \n
  • 自定义类类型

    \n
    1
    2
    3
    <insert id="save" parameterType="com.ajream.entity.Account">
    \tinsert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})
    </insert>
    \n
  • \n
\n

方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 param1, param2 …或者 arg0, arg1 … 【不同mybatis版本写可能不同,要注意param下标是从1开始的】

\n

例如:

\n

这是方法:

\n
1
public Account findByNameAndAge(name, age);
\n

则mapper如下

\n
1
2
3
4

<select id="findByNameAndAge" resultType="com.ajream.entity.Account">
select * from t_acount where username=#{param1} and age=#{param2}
</select>
\n

resultType:返回类型

用法与parameterType相同

\n

【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是int

\n
    \n
  • 基本数据类型
  • \n
  • 包装类
  • \n
  • String类型
  • \n
  • 自定义类类型
  • \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

mapper.xml

在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement

\n

因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据

\n

parameterType: 参数数据类型

    \n
  • 基本数据类型,int, long, …

    \n
    1
    2
    3
    <delete id="deleteById" parameterType="int">
    delete from t_acount where id = #{id}
    </delete>
    \n
  • \n
\n
    \n
  • String类型: java.lang.String

    \n
    1
    2
    3
    <select id="findByName" resultType="com.ajream.entity.Account" parameterType="java.lang.String">
    select * from t_acount where username=#{username}
    </select>
    \n
  • \n
  • 包装类,如:java.lang.Integerjava.lang.Long

    \n
  • \n
  • 自定义类类型

    \n
    1
    2
    3
    <insert id="save" parameterType="com.ajream.entity.Account">
    \tinsert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})
    </insert>
    \n
  • \n
\n

方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 param1, param2 …或者 arg0, arg1 … 【不同mybatis版本写可能不同,要注意param下标是从1开始的】

\n

例如:

\n

这是方法:

\n
1
public Account findByNameAndAge(name, age);
\n

则mapper如下

\n
1
2
3
4

<select id="findByNameAndAge" resultType="com.ajream.entity.Account">
select * from t_acount where username=#{param1} and age=#{param2}
</select>
\n

resultType:返回类型

用法与parameterType相同

\n

【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是int

\n
    \n
  • 基本数据类型
  • \n
  • 包装类
  • \n
  • String类型
  • \n
  • 自定义类类型
  • \n
\n"},{"title":"mybatis动态SQL(七)","description":"mybatis动态生成sql语句","abbrlink":"6e81c43e","date":"2021-08-27T10:23:10.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。\n\n那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。\n\n\n\n对于这样一张表,要根据 `id/username/passwd/age`来选出指定的一条记录,用sql语句可能会这样写:\n\n```sql\nselect * from t_acount\nwhere id=7 and username=\"xiaoBai\" and passwd=\"12345xb\" and age=24;\n```\n\n![image-20210828220529310](https://gitee.com/ajream/images/raw/master/img/20210828220537_image-20210828220529310.png)\n\n\n\n在Java中需要这样:\n\n接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\npublic interface AccountDao {\n Account findByAccount(Account account);\n}\n\n```\n\n\n\n在mapper文件中:\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class AccountDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n Account account = new Account();\n account.setId(7);\n account.setAge(24);\n account.setName(\"xiaoBai\");\n account.setPasswd(\"12345xb\");\n\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n Account account1 = accountDao.findByAccount(account);\n System.out.println(account1);\n }\n}\n\n```\n\n\n\n假如,在下面这句代码中\n\n```xml\n\n```\n\n\n\n某一个属性如`passwd` 值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式\n\n\n\n\n\n### if标签\n\n```xml\n\n```\n\n\n\n\n\n这样写我们可以看到,如果 `passwd` 等于 null,那么查询语句为:\n\n```\nselect * from t_acount where id=#{id} and username=#{name} and age=#{age}\n```\n\n但是如果 `id` 为空呢?那么查询语句为:\n\nselect * from t_acount where and username=#{name} and age=#{age}\n\n这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句\n\n\n\n### where/if标签\n\n```xml\n\n```\n\n\n\n这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个`where`,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。\n\n\n\n\n\n### choose/when-otherwise标签\n\n有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句\n\n```xml\n\n```\n\n\n\n也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件\n\n- 如果 id 不为空,那么查询语句为:\n\n ```\n select * from t_acount where id=#{id}\n ```\n\n \n\n- 如果 id 为空,那么看name 是否为空,如果不为空,那么语句为:\n\n ```\n select * from user where username=#{name}\n ```\n\n- 如果 username 为空,passwd不为空,那么查询语句为:\n\n ```\n select * from user where passwd=#{passwd}\n ```\n\n- 如果前面3个均为空,那么查询语句为:\n\n ```\n select * from user where age=#{age}\n ```\n\n \n\n\n\n\n\n### trim标签\n\n\n\ntrim标记是一个格式化的标记,可以完成set或者是where标记的功能\n\n用 trim 改写前面的 where-if 语句\n\n```xml\n\n```\n\nprefix: 表示添加前缀 `where`\n\nprefixOverrides: 去掉第一个`and`或者是`or`\n\n\n\n### set/if标签\n\n\n\n```xml\n\n update t_acount\n \n \n username = #{name},\n \n \n passwd=#{passwd}\n \n \n \n where id=#{id}\n\n```\n\n\n\n这样写,如果第一个条件 name 为空,那么 sql 语句为\n\n```\nupdate t_acount set passwd=#{passwd} where id=#{id}\n```\n\n如果第两个条件都不为空,那么 sql 语句为\n\n```\nupdate t_acount set username=#{name}, passwd=#{passwd} where id=#{id}\n```\n\n\n\n\n\n### sql(片段)标签\n\n\n\n有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。\n\n比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:\n\n```xml\n\n\n \n AND username = #{name}\n \n \n AND passwd = #{passwd}\n \n\n```\n\n\n\n引用sql片段\n\n\n\n```xml\n\n```\n\n\n\n\n\n### foreach标签\n\n 需求:我们需要查询 user 表中 id 分别为1,2,3的用户\n\n sql语句:\n\n```sql\nselect * from user where id=1 or id=2 or id=3;\nselect * from user where id in (1,2,3);\n```\n\n\n\n建立一个 UserVo 类,里面封装一个 `List ids` 的属性\n\n\n\n```java\npackage com.ajream.entity;\n \nimport java.util.List;\nimport lombok.Data;\n\n@Data\npublic class UserVo {\n //封装多个用户的id\n private List ids;\n \n}  \n```\n\n用 foreach 来改写 `select * from user where id=1 or id=2 or id=3`\n\n\n\n```xml\n\n```\n\n\n\n\n\n用 foreach 来改写 `select * from user where id in (1,2,3)`\n\n```xml\n\n```\n\n\n\n\n\n总结:\n\n动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。\n\n\n\n\n\n","source":"_posts/Mybatis/mybatis动态sql(七).md","raw":"---\ntitle: mybatis动态SQL(七)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: mybatis动态生成sql语句\nabbrlink: 6e81c43e\ndate: 2021-08-27 18:23:10\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。\n\n那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。\n\n\n\n对于这样一张表,要根据 `id/username/passwd/age`来选出指定的一条记录,用sql语句可能会这样写:\n\n```sql\nselect * from t_acount\nwhere id=7 and username=\"xiaoBai\" and passwd=\"12345xb\" and age=24;\n```\n\n![image-20210828220529310](https://gitee.com/ajream/images/raw/master/img/20210828220537_image-20210828220529310.png)\n\n\n\n在Java中需要这样:\n\n接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\npublic interface AccountDao {\n Account findByAccount(Account account);\n}\n\n```\n\n\n\n在mapper文件中:\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class AccountDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n Account account = new Account();\n account.setId(7);\n account.setAge(24);\n account.setName(\"xiaoBai\");\n account.setPasswd(\"12345xb\");\n\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n Account account1 = accountDao.findByAccount(account);\n System.out.println(account1);\n }\n}\n\n```\n\n\n\n假如,在下面这句代码中\n\n```xml\n\n```\n\n\n\n某一个属性如`passwd` 值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式\n\n\n\n\n\n### if标签\n\n```xml\n\n```\n\n\n\n\n\n这样写我们可以看到,如果 `passwd` 等于 null,那么查询语句为:\n\n```\nselect * from t_acount where id=#{id} and username=#{name} and age=#{age}\n```\n\n但是如果 `id` 为空呢?那么查询语句为:\n\nselect * from t_acount where and username=#{name} and age=#{age}\n\n这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句\n\n\n\n### where/if标签\n\n```xml\n\n```\n\n\n\n这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个`where`,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。\n\n\n\n\n\n### choose/when-otherwise标签\n\n有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句\n\n```xml\n\n```\n\n\n\n也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件\n\n- 如果 id 不为空,那么查询语句为:\n\n ```\n select * from t_acount where id=#{id}\n ```\n\n \n\n- 如果 id 为空,那么看name 是否为空,如果不为空,那么语句为:\n\n ```\n select * from user where username=#{name}\n ```\n\n- 如果 username 为空,passwd不为空,那么查询语句为:\n\n ```\n select * from user where passwd=#{passwd}\n ```\n\n- 如果前面3个均为空,那么查询语句为:\n\n ```\n select * from user where age=#{age}\n ```\n\n \n\n\n\n\n\n### trim标签\n\n\n\ntrim标记是一个格式化的标记,可以完成set或者是where标记的功能\n\n用 trim 改写前面的 where-if 语句\n\n```xml\n\n```\n\nprefix: 表示添加前缀 `where`\n\nprefixOverrides: 去掉第一个`and`或者是`or`\n\n\n\n### set/if标签\n\n\n\n```xml\n\n update t_acount\n \n \n username = #{name},\n \n \n passwd=#{passwd}\n \n \n \n where id=#{id}\n\n```\n\n\n\n这样写,如果第一个条件 name 为空,那么 sql 语句为\n\n```\nupdate t_acount set passwd=#{passwd} where id=#{id}\n```\n\n如果第两个条件都不为空,那么 sql 语句为\n\n```\nupdate t_acount set username=#{name}, passwd=#{passwd} where id=#{id}\n```\n\n\n\n\n\n### sql(片段)标签\n\n\n\n有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。\n\n比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:\n\n```xml\n\n\n \n AND username = #{name}\n \n \n AND passwd = #{passwd}\n \n\n```\n\n\n\n引用sql片段\n\n\n\n```xml\n\n```\n\n\n\n\n\n### foreach标签\n\n 需求:我们需要查询 user 表中 id 分别为1,2,3的用户\n\n sql语句:\n\n```sql\nselect * from user where id=1 or id=2 or id=3;\nselect * from user where id in (1,2,3);\n```\n\n\n\n建立一个 UserVo 类,里面封装一个 `List ids` 的属性\n\n\n\n```java\npackage com.ajream.entity;\n \nimport java.util.List;\nimport lombok.Data;\n\n@Data\npublic class UserVo {\n //封装多个用户的id\n private List ids;\n \n}  \n```\n\n用 foreach 来改写 `select * from user where id=1 or id=2 or id=3`\n\n\n\n```xml\n\n```\n\n\n\n\n\n用 foreach 来改写 `select * from user where id in (1,2,3)`\n\n```xml\n\n```\n\n\n\n\n\n总结:\n\n动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。\n\n\n\n\n\n","slug":"Mybatis/mybatis动态sql(七)","published":1,"updated":"2021-08-29T14:45:24.576Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pm003eakve82ox37f6","content":"

前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。

\n

那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。

\n

对于这样一张表,要根据 id/username/passwd/age来选出指定的一条记录,用sql语句可能会这样写:

\n
1
2
select * from t_acount
where id=7 and username="xiaoBai" and passwd="12345xb" and age=24;
\n

\"image-20210828220529310\"

\n

在Java中需要这样:

\n

接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Account;

public interface AccountDao {
Account findByAccount(Account account);
}

\n

在mapper文件中:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ajream.dao.AccountDao">
<resultMap id="resultAccount" type="com.ajream.entity.Account">
<id column="id" property="id"/>
<result column="passwd" property="passwd"/>
<result column="username" property="name"/>
<result column="age" property="age"/>
</resultMap>
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}
</select>
</mapper>
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.ajream.test;

import com.ajream.dao.AccountDao;
import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class AccountDaoTest {
public static void main(String[] args) {
InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

Account account = new Account();
account.setId(7);
account.setAge(24);
account.setName("xiaoBai");
account.setPasswd("12345xb");

AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

Account account1 = accountDao.findByAccount(account);
System.out.println(account1);
}
}

\n

假如,在下面这句代码中

\n
1
2
3
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
\tselect * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}
</select>
\n

某一个属性如passwd 值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式

\n

if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount where
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</select>
\n

这样写我们可以看到,如果 passwd 等于 null,那么查询语句为:

\n
1
select * from t_acount where id=#{id} and username=#{name} and age=#{age}
\n

但是如果 id 为空呢?那么查询语句为:

\n

select * from t_acount where and username=#{name} and age=#{age}

\n

这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句

\n

where/if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<where>
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</where>
</select>
\n

这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个where,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。

\n

choose/when-otherwise标签

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<where>
<choose>
<when test="id != 0">
\tid=#{id}
</when>
<when test="name !='' and name != null">
\tand username=#{name}
</when>
<when test="passwd!='' and passwd!=null">
\tand passwd=#{passwd}
</when>
<otherwise>
and age=#{age}
</otherwise>
</choose>
</where>
</select>
\n

也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件

\n
    \n
  • 如果 id 不为空,那么查询语句为:

    \n
    1
    select * from t_acount where id=#{id}
    \n
  • \n
\n
    \n
  • 如果 id 为空,那么看name 是否为空,如果不为空,那么语句为:

    \n
    1
    select * from user where username=#{name}
    \n
  • \n
  • 如果 username 为空,passwd不为空,那么查询语句为:

    \n
    1
    select * from user where passwd=#{passwd}
    \n
  • \n
  • 如果前面3个均为空,那么查询语句为:

    \n
    1
    select * from user where age=#{age}
    \n
  • \n
\n

trim标签

trim标记是一个格式化的标记,可以完成set或者是where标记的功能

\n

用 trim 改写前面的 where-if 语句

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<trim prefix="where" prefixOverrides="and | or">
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</trim>
</select>
\n

prefix: 表示添加前缀 where

\n

prefixOverrides: 去掉第一个and或者是or

\n

set/if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
<update id="update" parameterType="com.ajream.entity.Account">
update t_acount
<set>
<if test="name != null and username != ''">
username = #{name},
</if>
<if test="passwd != null and passwd != ''">
passwd=#{passwd}
</if>
</set>

where id=#{id}
</update>
\n

这样写,如果第一个条件 name 为空,那么 sql 语句为

\n
1
update t_acount set passwd=#{passwd} where id=#{id}
\n

如果第两个条件都不为空,那么 sql 语句为

\n
1
update t_acount set username=#{name}, passwd=#{passwd} where id=#{id}
\n

sql(片段)标签

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

\n

比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:

\n
1
2
3
4
5
6
7
8
9
<!-- 定义 sql 片段 -->
<sql id="selectBySQL">
<if test="name != null and name != ''">
AND username = #{name}
</if>
<if test="passwd != null and passwd != ''">
AND passwd = #{passwd}
</if>
</sql>
\n

引用sql片段

\n
1
2
3
4
5
6
7
8
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<trim prefix="where" prefixOverrides="and | or">
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="selectBySQL"/>
<!-- 在这里还可以引用其他的 sql 片段 -->
</trim>
</select>
\n

foreach标签

 需求:我们需要查询 user 表中 id 分别为1,2,3的用户

\n

 sql语句:

\n
1
2
select * from user where id=1 or id=2 or id=3;
select * from user where id in (1,2,3);
\n

建立一个 UserVo 类,里面封装一个 List<Integer> ids 的属性

\n
1
2
3
4
5
6
7
8
9
10
11
package com.ajream.entity;

import java.util.List;
import lombok.Data;

@Data
public class UserVo {
//封装多个用户的id
private List<Integer> ids;

}  
\n

用 foreach 来改写 select * from user where id=1 or id=2 or id=3

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="selectUserByListId" parameterType="com.ajream.entity.UserVo" resultType="com.ajream.entity.User">
select * from user
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from user where 1=1 and (id=1 or id=2 or id=3)
-->
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
\n

用 foreach 来改写 select * from user where id in (1,2,3)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.ys.po.User">
select * from user
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from user where 1=1 and id in (1,2,3)
-->
<foreach collection="ids" item="id" open="and id in (" close=") " separator=",">
#{id}
</foreach>
</where>
</select>
\n

总结:

\n

动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。

\n

那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。

\n

对于这样一张表,要根据 id/username/passwd/age来选出指定的一条记录,用sql语句可能会这样写:

\n
1
2
select * from t_acount
where id=7 and username="xiaoBai" and passwd="12345xb" and age=24;
\n

\"image-20210828220529310\"

\n

在Java中需要这样:

\n

接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Account;

public interface AccountDao {
Account findByAccount(Account account);
}

\n

在mapper文件中:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ajream.dao.AccountDao">
<resultMap id="resultAccount" type="com.ajream.entity.Account">
<id column="id" property="id"/>
<result column="passwd" property="passwd"/>
<result column="username" property="name"/>
<result column="age" property="age"/>
</resultMap>
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}
</select>
</mapper>
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.ajream.test;

import com.ajream.dao.AccountDao;
import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class AccountDaoTest {
public static void main(String[] args) {
InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

Account account = new Account();
account.setId(7);
account.setAge(24);
account.setName("xiaoBai");
account.setPasswd("12345xb");

AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

Account account1 = accountDao.findByAccount(account);
System.out.println(account1);
}
}

\n

假如,在下面这句代码中

\n
1
2
3
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
\tselect * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}
</select>
\n

某一个属性如passwd 值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式

\n

if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount where
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</select>
\n

这样写我们可以看到,如果 passwd 等于 null,那么查询语句为:

\n
1
select * from t_acount where id=#{id} and username=#{name} and age=#{age}
\n

但是如果 id 为空呢?那么查询语句为:

\n

select * from t_acount where and username=#{name} and age=#{age}

\n

这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句

\n

where/if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<where>
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</where>
</select>
\n

这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个where,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。

\n

choose/when-otherwise标签

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<where>
<choose>
<when test="id != 0">
\tid=#{id}
</when>
<when test="name !='' and name != null">
\tand username=#{name}
</when>
<when test="passwd!='' and passwd!=null">
\tand passwd=#{passwd}
</when>
<otherwise>
and age=#{age}
</otherwise>
</choose>
</where>
</select>
\n

也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件

\n
    \n
  • 如果 id 不为空,那么查询语句为:

    \n
    1
    select * from t_acount where id=#{id}
    \n
  • \n
\n
    \n
  • 如果 id 为空,那么看name 是否为空,如果不为空,那么语句为:

    \n
    1
    select * from user where username=#{name}
    \n
  • \n
  • 如果 username 为空,passwd不为空,那么查询语句为:

    \n
    1
    select * from user where passwd=#{passwd}
    \n
  • \n
  • 如果前面3个均为空,那么查询语句为:

    \n
    1
    select * from user where age=#{age}
    \n
  • \n
\n

trim标签

trim标记是一个格式化的标记,可以完成set或者是where标记的功能

\n

用 trim 改写前面的 where-if 语句

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<trim prefix="where" prefixOverrides="and | or">
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</trim>
</select>
\n

prefix: 表示添加前缀 where

\n

prefixOverrides: 去掉第一个and或者是or

\n

set/if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
<update id="update" parameterType="com.ajream.entity.Account">
update t_acount
<set>
<if test="name != null and username != ''">
username = #{name},
</if>
<if test="passwd != null and passwd != ''">
passwd=#{passwd}
</if>
</set>

where id=#{id}
</update>
\n

这样写,如果第一个条件 name 为空,那么 sql 语句为

\n
1
update t_acount set passwd=#{passwd} where id=#{id}
\n

如果第两个条件都不为空,那么 sql 语句为

\n
1
update t_acount set username=#{name}, passwd=#{passwd} where id=#{id}
\n

sql(片段)标签

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

\n

比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:

\n
1
2
3
4
5
6
7
8
9
<!-- 定义 sql 片段 -->
<sql id="selectBySQL">
<if test="name != null and name != ''">
AND username = #{name}
</if>
<if test="passwd != null and passwd != ''">
AND passwd = #{passwd}
</if>
</sql>
\n

引用sql片段

\n
1
2
3
4
5
6
7
8
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<trim prefix="where" prefixOverrides="and | or">
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="selectBySQL"/>
<!-- 在这里还可以引用其他的 sql 片段 -->
</trim>
</select>
\n

foreach标签

 需求:我们需要查询 user 表中 id 分别为1,2,3的用户

\n

 sql语句:

\n
1
2
select * from user where id=1 or id=2 or id=3;
select * from user where id in (1,2,3);
\n

建立一个 UserVo 类,里面封装一个 List<Integer> ids 的属性

\n
1
2
3
4
5
6
7
8
9
10
11
package com.ajream.entity;

import java.util.List;
import lombok.Data;

@Data
public class UserVo {
//封装多个用户的id
private List<Integer> ids;

}  
\n

用 foreach 来改写 select * from user where id=1 or id=2 or id=3

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="selectUserByListId" parameterType="com.ajream.entity.UserVo" resultType="com.ajream.entity.User">
select * from user
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from user where 1=1 and (id=1 or id=2 or id=3)
-->
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
\n

用 foreach 来改写 select * from user where id in (1,2,3)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.ys.po.User">
select * from user
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from user where 1=1 and id in (1,2,3)
-->
<foreach collection="ids" item="id" open="and id in (" close=") " separator=",">
#{id}
</foreach>
</where>
</select>
\n

总结:

\n

动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。

\n"},{"title":"mybatis延迟加载-缓存机制(六)","description":"mybatis通过延迟加载、缓存机制来提高程序运行效率","abbrlink":"3b32a20d","date":"2021-08-27T07:43:16.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n\n\n## 延迟加载\n\nMyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。\n\nMyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。\n\n\n\n### 加载时机\n\n直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。\n\n侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。\n\n深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。\n\n{% note info flat %}\n\n注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。\n因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。\n\n{% endnote %}\n\n\n### 侵入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载\n\n ```xml\n \n \n \t\n \n ```\n2. 不调用主加载对象时只有一条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231810.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231811.png)\n\n3. 调用主加载对象的信息时会产生两条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231812.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231813.png)\n\n\n### 深入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载\n\n ```xml\n \n \n \n \n ```\n2. 调用主加载对象时不会执行第二条加载SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231814.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231915.png)\n\n3. 调用关联对象详细信息时会执行第二次查询\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231916.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231917.png)\n\n\n\n\n\n\n\n## 缓存\n\n\n\nMyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。\n\nMyBatis提供一级缓存和二级缓存的机制。\n\n\n\n### 一级缓存\n\n一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。\n\n当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。\n\n\n\n### 二级缓存\n\n二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件中的sql语句,多个SqlSession类的实例对象可以共用二级缓存\n\n二级缓存是跨SqlSession的。\n\n一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。\n\n开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true\n\n```xml\n \n \n \n```\n\n\n\n![image-20210829001744654](https://gitee.com/ajream/images/raw/master/img/20210829001748_image-20210829001744654.png)\n\n\n\nMyBatis的缓存模式如图所示:\n\n\n\n![image-20210829001805748](https://gitee.com/ajream/images/raw/master/img/20210829001807_image-20210829001805748.png)\n\n\n\n\n\n注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。\n\n","source":"_posts/Mybatis/mybatis延迟加载(六).md","raw":"---\ntitle: mybatis延迟加载-缓存机制(六)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: mybatis通过延迟加载、缓存机制来提高程序运行效率\nabbrlink: 3b32a20d\ndate: 2021-08-27 15:43:16\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n\n\n## 延迟加载\n\nMyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。\n\nMyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。\n\n\n\n### 加载时机\n\n直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。\n\n侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。\n\n深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。\n\n{% note info flat %}\n\n注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。\n因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。\n\n{% endnote %}\n\n\n### 侵入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载\n\n ```xml\n \n \n \t\n \n ```\n2. 不调用主加载对象时只有一条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231810.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231811.png)\n\n3. 调用主加载对象的信息时会产生两条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231812.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231813.png)\n\n\n### 深入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载\n\n ```xml\n \n \n \n \n ```\n2. 调用主加载对象时不会执行第二条加载SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231814.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231915.png)\n\n3. 调用关联对象详细信息时会执行第二次查询\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231916.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231917.png)\n\n\n\n\n\n\n\n## 缓存\n\n\n\nMyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。\n\nMyBatis提供一级缓存和二级缓存的机制。\n\n\n\n### 一级缓存\n\n一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。\n\n当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。\n\n\n\n### 二级缓存\n\n二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件中的sql语句,多个SqlSession类的实例对象可以共用二级缓存\n\n二级缓存是跨SqlSession的。\n\n一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。\n\n开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true\n\n```xml\n \n \n \n```\n\n\n\n![image-20210829001744654](https://gitee.com/ajream/images/raw/master/img/20210829001748_image-20210829001744654.png)\n\n\n\nMyBatis的缓存模式如图所示:\n\n\n\n![image-20210829001805748](https://gitee.com/ajream/images/raw/master/img/20210829001807_image-20210829001805748.png)\n\n\n\n\n\n注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。\n\n","slug":"Mybatis/mybatis延迟加载(六)","published":1,"updated":"2021-08-30T13:49:53.564Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pn003hakvebel7gurk","content":"

延迟加载

MyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。

\n

MyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。

\n

加载时机

直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。

\n

侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。

\n

深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。

\n

注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。
因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。

\n
\n

侵入式延迟加载

    \n
  1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载

    \n
    1
    2
    3
    4
    5
    6
    7
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--配置侵入式延迟加载 默认为false(深度加载)
    侵入式:默认只会执行主加载SQL,那么当访问主加载对象的详细信息时才会执行关联对象的SQL查询
    深度延迟:默认只执行主加载SQL,那么当调用到主加载对象中关联对象的信息时才会执行关联对象的SQL查询
    -->\t
    <setting name="aggressiveLazyLoading" value="true"/>
  2. \n
  3. 不调用主加载对象时只有一条SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  4. \n
  5. 调用主加载对象的信息时会产生两条SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  6. \n
\n

深入式延迟加载

    \n
  1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载

    \n
    1
    2
    3
    4
    5
    6
    7
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--配置侵入式延迟加载 默认为false(深度加载)
    侵入式:默认只会执行主加载SQL,那么当访问主加载对象的详细信息时才会执行关联对象的SQL查询
    深度延迟:默认只执行主加载SQL,那么当调用到主加载对象中关联对象的信息时才会执行关联对象的SQL查询
    -->
    <setting name="aggressiveLazyLoading" value="false"/>
  2. \n
  3. 调用主加载对象时不会执行第二条加载SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  4. \n
  5. 调用关联对象详细信息时会执行第二次查询

    \n

    \"img\"

    \n

    \"img\"

    \n
  6. \n
\n

缓存

MyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。

\n

MyBatis提供一级缓存和二级缓存的机制。

\n

一级缓存

一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。

\n

当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。

\n

二级缓存

二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件中的sql语句,多个SqlSession类的实例对象可以共用二级缓存

\n

二级缓存是跨SqlSession的。

\n

一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。

\n

开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true

\n
1
2
3
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
\n

\"image-20210829001744654\"

\n

MyBatis的缓存模式如图所示:

\n

\"image-20210829001805748\"

\n

注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

延迟加载

MyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。

\n

MyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。

\n

加载时机

直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。

\n

侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。

\n

深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。

\n

注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。
因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。

\n
\n

侵入式延迟加载

    \n
  1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载

    \n
    1
    2
    3
    4
    5
    6
    7
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--配置侵入式延迟加载 默认为false(深度加载)
    侵入式:默认只会执行主加载SQL,那么当访问主加载对象的详细信息时才会执行关联对象的SQL查询
    深度延迟:默认只执行主加载SQL,那么当调用到主加载对象中关联对象的信息时才会执行关联对象的SQL查询
    -->\t
    <setting name="aggressiveLazyLoading" value="true"/>
  2. \n
  3. 不调用主加载对象时只有一条SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  4. \n
  5. 调用主加载对象的信息时会产生两条SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  6. \n
\n

深入式延迟加载

    \n
  1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载

    \n
    1
    2
    3
    4
    5
    6
    7
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--配置侵入式延迟加载 默认为false(深度加载)
    侵入式:默认只会执行主加载SQL,那么当访问主加载对象的详细信息时才会执行关联对象的SQL查询
    深度延迟:默认只执行主加载SQL,那么当调用到主加载对象中关联对象的信息时才会执行关联对象的SQL查询
    -->
    <setting name="aggressiveLazyLoading" value="false"/>
  2. \n
  3. 调用主加载对象时不会执行第二条加载SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  4. \n
  5. 调用关联对象详细信息时会执行第二次查询

    \n

    \"img\"

    \n

    \"img\"

    \n
  6. \n
\n

缓存

MyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。

\n

MyBatis提供一级缓存和二级缓存的机制。

\n

一级缓存

一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。

\n

当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。

\n

二级缓存

二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件中的sql语句,多个SqlSession类的实例对象可以共用二级缓存

\n

二级缓存是跨SqlSession的。

\n

一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。

\n

开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true

\n
1
2
3
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
\n

\"image-20210829001744654\"

\n

MyBatis的缓存模式如图所示:

\n

\"image-20210829001805748\"

\n

注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。

\n"},{"title":"mybatis介绍与使用(一)","date":"2021-08-25T02:30:46.000Z","abbrlink":"78903d87","description":"关于mybatis的简介与使用(第一个Mybatis程序)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n## 1、简介\n\n### 什么是 MyBatis\n\n​\tMyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。\n\n\n\n\n\n## 2、第一个MyBatis项目\n\n[项目地址mybatis-01](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master/)\n\n### (1)安装库\n\n​\t如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:\n\n```xml\n\n mysql\n mysql-connector-java\n 8.0.26\n \n\n\n\n org.mybatis\n mybatis\n 3.5.6\n\n\n\n\n junit\n junit\n 4.13\n test\n\n```\n\n### (2)创建MyBatis工具类\n\n为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据\n\ncom/ajream/utils/MybatisUtil.java\n\n```java\npackage com.ajream.utils;\n\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class MybatisUtil {\n\n private static SqlSessionFactory sqlSessionFactory;\n\n static {\n try {\n //mybatis-config.xml是mybatis配置文件,用于配置MySQL信息\n String resource = \"mybatis-config.xml\"; \n InputStream inputStream = Resources.getResourceAsStream(resource);\n sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);\n }catch (IOException e){\n e.printStackTrace();\n }\n }\n \n //生成Mysql的会话\n public static SqlSession getSqlSesion(){\n return sqlSessionFactory.openSession(); \n }\n\n}\n\n```\n\n\n\n配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)\n\n```xml\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n### (3)存放数据的容器类\n\n这是某个数据库school中student表中的数据\n\n![image-20210813000155615](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-01-58_image-20210813000155615.png)\n\n\n\n所以这里创建一个User类来表示数据库中的字段\n\ncom/ajream/pojo/User.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String id;\n private String name;\n private int age;\n\n public User() {\n }\n\n public User(String id, String name, int age) {\n this.id = id;\n this.name = name;\n this.age = age;\n }\n\n public String getId() {\n return id;\n }\n\n public void setId(String id) {\n this.id = id;\n }\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public int getAge() {\n return age;\n }\n\n public void setAge(int age) {\n this.age = age;\n }\n \n @Override\n public String toString() {\n return \"User{\" +\n \"id='\" + id + '\\'' +\n \", name='\" + name + '\\'' +\n \", age=\" + age +\n '}';\n }\n}\n\n```\n\n\n\n### (4)创建一个接口\n\n(用于获取并存放拿到的数据)\n\ncom/ajream/dao/UserDao.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\n\nimport java.util.List;\n\npublic interface UserDao {\n List getUserList();\n}\n\n```\n\n\n\n\n\n### (5)实现这个接口\n\n(即用sql语句获取数据)\n\n通常我们会创建一个 `UserDaoImpl` 来实现这个接口,但使用mybatis可以通过配置文件来代理实现:\n\ncom/ajream/dao/UserMapper.xml\n\n```xml\n\n\n\n\n\n \n \n \n \n\n \n\n```\n\n> Mapper.xml用于将MySQL数据库与Java类相联系起来\n>\n> - insert标签表示插入操作\n> - select标签是查询操作\n> - update是修改操作\n> - delete标签是删除操作\n\n\n\n> 最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:\n>\n> ```xml\n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n### (6)测试\n\n以上配置完成后,可以写个test测试程序来取出数据库中的数据了\n\n\n\ncom/ajream/dao/UserDaoTest.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\nimport com.ajream.utils.MybatisUtil;\nimport org.apache.ibatis.session.SqlSession;\nimport org.junit.Test;\n\nimport java.util.List;\n\n\npublic class UserDaoTest {\n\n @Test\n public void userDaoTest(){\n\n //获取MySQL会话\n SqlSession sqlSession = MybatisUtil.getSqlSesion();\n \n UserDao userDao = sqlSession.getMapper(UserDao.class);\n \n List userList = userDao.getUserList();\n\n for(User u: userList){\n System.out.println(u);\n }\n\n sqlSession.close();\n }\n}\n\n```\n\n\n\n### (7)注意\n\nidea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:\n\n```xml\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\n运行结果:\n\n\n\n![image-20210813001800000](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-18-01_image-20210813001800000.png)\n\n","source":"_posts/Mybatis/mybatis简单介绍与使用(一).md","raw":"---\ntitle: mybatis介绍与使用(一)\ndate: 2021-08-25 10:30:46\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\nabbrlink: 78903d87\ndescription: 关于mybatis的简介与使用(第一个Mybatis程序)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n## 1、简介\n\n### 什么是 MyBatis\n\n​\tMyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。\n\n\n\n\n\n## 2、第一个MyBatis项目\n\n[项目地址mybatis-01](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master/)\n\n### (1)安装库\n\n​\t如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:\n\n```xml\n\n mysql\n mysql-connector-java\n 8.0.26\n \n\n\n\n org.mybatis\n mybatis\n 3.5.6\n\n\n\n\n junit\n junit\n 4.13\n test\n\n```\n\n### (2)创建MyBatis工具类\n\n为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据\n\ncom/ajream/utils/MybatisUtil.java\n\n```java\npackage com.ajream.utils;\n\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class MybatisUtil {\n\n private static SqlSessionFactory sqlSessionFactory;\n\n static {\n try {\n //mybatis-config.xml是mybatis配置文件,用于配置MySQL信息\n String resource = \"mybatis-config.xml\"; \n InputStream inputStream = Resources.getResourceAsStream(resource);\n sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);\n }catch (IOException e){\n e.printStackTrace();\n }\n }\n \n //生成Mysql的会话\n public static SqlSession getSqlSesion(){\n return sqlSessionFactory.openSession(); \n }\n\n}\n\n```\n\n\n\n配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)\n\n```xml\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n### (3)存放数据的容器类\n\n这是某个数据库school中student表中的数据\n\n![image-20210813000155615](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-01-58_image-20210813000155615.png)\n\n\n\n所以这里创建一个User类来表示数据库中的字段\n\ncom/ajream/pojo/User.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String id;\n private String name;\n private int age;\n\n public User() {\n }\n\n public User(String id, String name, int age) {\n this.id = id;\n this.name = name;\n this.age = age;\n }\n\n public String getId() {\n return id;\n }\n\n public void setId(String id) {\n this.id = id;\n }\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public int getAge() {\n return age;\n }\n\n public void setAge(int age) {\n this.age = age;\n }\n \n @Override\n public String toString() {\n return \"User{\" +\n \"id='\" + id + '\\'' +\n \", name='\" + name + '\\'' +\n \", age=\" + age +\n '}';\n }\n}\n\n```\n\n\n\n### (4)创建一个接口\n\n(用于获取并存放拿到的数据)\n\ncom/ajream/dao/UserDao.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\n\nimport java.util.List;\n\npublic interface UserDao {\n List getUserList();\n}\n\n```\n\n\n\n\n\n### (5)实现这个接口\n\n(即用sql语句获取数据)\n\n通常我们会创建一个 `UserDaoImpl` 来实现这个接口,但使用mybatis可以通过配置文件来代理实现:\n\ncom/ajream/dao/UserMapper.xml\n\n```xml\n\n\n\n\n\n \n \n \n \n\n \n\n```\n\n> Mapper.xml用于将MySQL数据库与Java类相联系起来\n>\n> - insert标签表示插入操作\n> - select标签是查询操作\n> - update是修改操作\n> - delete标签是删除操作\n\n\n\n> 最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:\n>\n> ```xml\n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n### (6)测试\n\n以上配置完成后,可以写个test测试程序来取出数据库中的数据了\n\n\n\ncom/ajream/dao/UserDaoTest.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\nimport com.ajream.utils.MybatisUtil;\nimport org.apache.ibatis.session.SqlSession;\nimport org.junit.Test;\n\nimport java.util.List;\n\n\npublic class UserDaoTest {\n\n @Test\n public void userDaoTest(){\n\n //获取MySQL会话\n SqlSession sqlSession = MybatisUtil.getSqlSesion();\n \n UserDao userDao = sqlSession.getMapper(UserDao.class);\n \n List userList = userDao.getUserList();\n\n for(User u: userList){\n System.out.println(u);\n }\n\n sqlSession.close();\n }\n}\n\n```\n\n\n\n### (7)注意\n\nidea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:\n\n```xml\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\n运行结果:\n\n\n\n![image-20210813001800000](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-18-01_image-20210813001800000.png)\n\n","slug":"Mybatis/mybatis简单介绍与使用(一)","published":1,"updated":"2021-08-29T14:44:46.760Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6po003kakveajnw0fjv","content":"

1、简介

什么是 MyBatis

​ MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

\n

2、第一个MyBatis项目

项目地址mybatis-01

\n

(1)安装库

​ 如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
<!--注意:由于本地系统安装了版本8的Mysql,所以这里使用版本8的驱动-->
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<!--junit是测试时用到的-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
\n

(2)创建MyBatis工具类

为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据

\n

com/ajream/utils/MybatisUtil.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.ajream.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MybatisUtil {

private static SqlSessionFactory sqlSessionFactory;

static {
try {
//mybatis-config.xml是mybatis配置文件,用于配置MySQL信息
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch (IOException e){
e.printStackTrace();
}
}

//生成Mysql的会话
public static SqlSession getSqlSesion(){
return sqlSessionFactory.openSession();
}

}

\n

配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置mybatis运行环境-->
<environments default="development">
<environment id="development">
<!--配置jdbc事务管理-->
<transactionManager type="JDBC"/>
<!--POOLED配置jdbc数据源连接池-->
<dataSource type="POOLED">

<!--注意value=”com.mysql.cj.jdbc.Driver“中的【cj】是MySQL版本8才要使用的-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>

<!--连接数据库用到的url,这里连接的database是school,根据自己情况修改-->
<property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>

<!--用户名和密码-->
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>

</dataSource>
</environment>
</environments>

<!--com/ajream/dao的路径下还有一个配置文件,此处把它包含进来-->
<mappers>
<mapper resource="com/ajream/dao/UserMapper.xml"/>
</mappers>

</configuration>
\n

(3)存放数据的容器类

这是某个数据库school中student表中的数据

\n

\"image-20210813000155615\"

\n

所以这里创建一个User类来表示数据库中的字段

\n

com/ajream/pojo/User.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.ajream.pojo;

public class User {
private String id;
private String name;
private int age;

public User() {
}

public User(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}

public String getId() {
return id;
}

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

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

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

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

\n

(4)创建一个接口

(用于获取并存放拿到的数据)

\n

com/ajream/dao/UserDao.java

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.dao;

import com.ajream.pojo.User;

import java.util.List;

public interface UserDao {
List<User> getUserList();
}

\n

(5)实现这个接口

(即用sql语句获取数据)

\n

通常我们会创建一个 UserDaoImpl 来实现这个接口,但使用mybatis可以通过配置文件来代理实现:

\n

com/ajream/dao/UserMapper.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--命名空间是接口名-->
<mapper namespace="com.ajream.dao.UserDao">

<!--getUserList是接口的方法名,returnType是方法返回的数据类型-->
<select id="getUserList" resultType="com.ajream.pojo.User">
select * from school.student
</select>

<!--还支持insert/update/delete标签

<insert id="..." parameterType="xxx" >
\tinsert into 表名() values()
</insert>
\t......
-->

</mapper>
\n
\n

Mapper.xml用于将MySQL数据库与Java类相联系起来

\n
    \n
  • insert标签表示插入操作
  • \n
  • select标签是查询操作
  • \n
  • update是修改操作
  • \n
  • delete标签是删除操作
  • \n
\n

最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:

\n
1
2
3
4
<mappers>
<mapper resource="com/ajream/dao/UserMapper.xml"/>
</mappers>

\n
\n

(6)测试

以上配置完成后,可以写个test测试程序来取出数据库中的数据了

\n

com/ajream/dao/UserDaoTest.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.ajream.dao;

import com.ajream.pojo.User;
import com.ajream.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;


public class UserDaoTest {

@Test
public void userDaoTest(){

//获取MySQL会话
SqlSession sqlSession = MybatisUtil.getSqlSesion();

UserDao userDao = sqlSession.getMapper(UserDao.class);

List<User> userList = userDao.getUserList();

for(User u: userList){
System.out.println(u);
}

sqlSession.close();
}
}

\n

(7)注意

idea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

运行结果:

\n

\"image-20210813001800000\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

1、简介

什么是 MyBatis

​ MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

\n

2、第一个MyBatis项目

项目地址mybatis-01

\n

(1)安装库

​ 如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
<!--注意:由于本地系统安装了版本8的Mysql,所以这里使用版本8的驱动-->
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<!--junit是测试时用到的-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
\n

(2)创建MyBatis工具类

为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据

\n

com/ajream/utils/MybatisUtil.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.ajream.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MybatisUtil {

private static SqlSessionFactory sqlSessionFactory;

static {
try {
//mybatis-config.xml是mybatis配置文件,用于配置MySQL信息
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch (IOException e){
e.printStackTrace();
}
}

//生成Mysql的会话
public static SqlSession getSqlSesion(){
return sqlSessionFactory.openSession();
}

}

\n

配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置mybatis运行环境-->
<environments default="development">
<environment id="development">
<!--配置jdbc事务管理-->
<transactionManager type="JDBC"/>
<!--POOLED配置jdbc数据源连接池-->
<dataSource type="POOLED">

<!--注意value=”com.mysql.cj.jdbc.Driver“中的【cj】是MySQL版本8才要使用的-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>

<!--连接数据库用到的url,这里连接的database是school,根据自己情况修改-->
<property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>

<!--用户名和密码-->
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>

</dataSource>
</environment>
</environments>

<!--com/ajream/dao的路径下还有一个配置文件,此处把它包含进来-->
<mappers>
<mapper resource="com/ajream/dao/UserMapper.xml"/>
</mappers>

</configuration>
\n

(3)存放数据的容器类

这是某个数据库school中student表中的数据

\n

\"image-20210813000155615\"

\n

所以这里创建一个User类来表示数据库中的字段

\n

com/ajream/pojo/User.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.ajream.pojo;

public class User {
private String id;
private String name;
private int age;

public User() {
}

public User(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}

public String getId() {
return id;
}

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

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

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

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

\n

(4)创建一个接口

(用于获取并存放拿到的数据)

\n

com/ajream/dao/UserDao.java

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.dao;

import com.ajream.pojo.User;

import java.util.List;

public interface UserDao {
List<User> getUserList();
}

\n

(5)实现这个接口

(即用sql语句获取数据)

\n

通常我们会创建一个 UserDaoImpl 来实现这个接口,但使用mybatis可以通过配置文件来代理实现:

\n

com/ajream/dao/UserMapper.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--命名空间是接口名-->
<mapper namespace="com.ajream.dao.UserDao">

<!--getUserList是接口的方法名,returnType是方法返回的数据类型-->
<select id="getUserList" resultType="com.ajream.pojo.User">
select * from school.student
</select>

<!--还支持insert/update/delete标签

<insert id="..." parameterType="xxx" >
\tinsert into 表名() values()
</insert>
\t......
-->

</mapper>
\n
\n

Mapper.xml用于将MySQL数据库与Java类相联系起来

\n
    \n
  • insert标签表示插入操作
  • \n
  • select标签是查询操作
  • \n
  • update是修改操作
  • \n
  • delete标签是删除操作
  • \n
\n

最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:

\n
1
2
3
4
<mappers>
<mapper resource="com/ajream/dao/UserMapper.xml"/>
</mappers>

\n
\n

(6)测试

以上配置完成后,可以写个test测试程序来取出数据库中的数据了

\n

com/ajream/dao/UserDaoTest.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.ajream.dao;

import com.ajream.pojo.User;
import com.ajream.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;


public class UserDaoTest {

@Test
public void userDaoTest(){

//获取MySQL会话
SqlSession sqlSession = MybatisUtil.getSqlSesion();

UserDao userDao = sqlSession.getMapper(UserDao.class);

List<User> userList = userDao.getUserList();

for(User u: userList){
System.out.println(u);
}

sqlSession.close();
}
}

\n

(7)注意

idea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

运行结果:

\n

\"image-20210813001800000\"

\n"},{"title":"mybatis介绍与使用(二)","description":"(一)的例子看不懂可以先看这个,也是一个入门mybatis的例子,该例子逻辑可能更清晰","abbrlink":"ab69c7ec","date":"2021-08-25T06:30:46.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n[项目地址mybatis-02](https://codechina.csdn.net/m0_46079750/mybatis-study)\n\n### 新建数据库表\n\n\n\n```sql\nUSE mybatis;\n\nCREATE TABLE t_acount(\n\tid INT PRIMARY KEY auto_increment,\n\tusername VARCHAR(11),\n\tpasswd VARCHAR(11),\n\tage INT\n)\n```\n\n\n\n### 配置mybatis开发环境\n\n\n\n使用maven创建新项目,在pom.xml导入依赖:\n\n```xml\n\n \n mysql\n mysql-connector-java\n 8.0.26\n \n\n \n org.mybatis\n mybatis\n 3.5.6\n \n\n \n junit\n junit\n 4.13\n test\n \n\n \n org.projectlombok\n lombok\n RELEASE\n compile\n \n\n```\n\n\n\n新建数据表对应的Account实体类\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n//@Data可以为类提供读写功能,从而不用写get、set方法; 会为类提供 equals()、hashCode()、toString() 方法。\n//@AllArgsConstructor 自动添加有参构造方法\n//@NoArgsConstructor 添加无参构造方法\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Account {\n private int id;\n private String username;\n private String passwd;\n private int age;\n}\n\n```\n\n\n\n在resources创建mybatis配置文件mybatis-config.xml\n\n首先要导入约束(注意这个与配置文件那个的是不同的)\n\n```xml\n\n\n```\n\nmybatis-config.xml:\n\n```xml\n\n\n\n\n\n\n \n \n\n \n\n \n\n \n \n \n \n \n\n \n \n\n```\n\n到此mybatis开发环境已经搭建好,接下来进行开发\n\n\n\n\n\n### Mybatis开发方式\n\n1. 原生接口方式\n2. mapper代理实现自定义接口方式\n\n\n\n\n\n> **先介绍第一种:原生接口方式**\n\n\n\nmybatis框架需要开发者自定义sql语句,写在Mapper.xml文件\n\n实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象\n\n\n\n\n\n(一)创建Mapper文件\n\n因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件\n\n\n\n![image-20210826131020613](https://gitee.com/ajream/images/raw/master/img/20210826131024_image-20210826131020613.png)\n\n\n\n\n\n首先也是要导入约束:\n\n```xml\n\n\n```\n\nAccountMapper.xml:\n\n```xml\n\n\n\n\n\n\n\n \n insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age})\n \n\n```\n\n说明:\n\n1. namespace通常设置为文件所在包名+文件名(无后缀)\n\n2. 可以把这个Mapper文件理解成一个java类,类名是 `AccountMapper`, 有一个方法 `save` ,可以传入参数,参数类型是 `com.ajream.entity.Account`\n\n 这个方法的作用是执行insert插入数据操作\n\n\n\n\n\n(二)注册Mapper文件\n\n在mybatis-config.xml配置文件中注册mapper文件, 添加:\n\n```xml\n\n\t\n\n```\n\n![image-20210826133054400](https://gitee.com/ajream/images/raw/master/img/20210826133057_image-20210826133054400.png)\n\n\n\n\n\n### 编写测试类进行运行\n\n1. 根据mybatis配置文件,使用工厂模式生成sqlSession\n2. 执行sql语句\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class MyTest {\n public static void main(String[] args) {\n //加载配置文件,获取sqlSession工厂对象\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis02-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n// 通过工厂对象获取sqlSession\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n String statement= \"com.ajream.mapper.AccountMapper.save\"; //用于加载mapper配置文件的save方法\n Account account = new Account(2,\"XiaoZhang\", \"56789xz\", 20);\n sqlSession.insert(statement, account); //执行sql语句\n sqlSession.commit(); //增、删、改 最后必须提交事务\n\n }\n}\n\n```\n\n\n\n\n\n\n\n注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件\n\n```xml\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\n\n\n如果报错:\n\n```\nCause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。\n```\n\n在pom.xml中添加语句:\n\n```xml\n\n UTF-8\n\n```\n\n![image-20210826150549895](https://gitee.com/ajream/images/raw/master/img/20210826150553_image-20210826150549895.png)\n\n\n\n\n\n\n\n> **第二种:Mapper代理实现自定义接口方式(推荐)**\n>\n> [项目地址mybatis-03](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master1)\n\n\n\n(一)创建接口:\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\nimport java.util.List;\n\n//增删改查\npublic interface AccountDao {\n int save(Account account);\n int update(Account account);\n int deleteById(int id);\n List findAll();\n Account findById(int id);\n}\n\n```\n\n\n\n\n\n(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过`insert/update/delete/select`标签来定义接口方法对应的sql语句\n\n\n\n规则:\n\n- namespace为接口的全类名\n\n- id为接口中对应的方法名\n\n- parameterType为接口中对应方法的参数类型\n\n- resultType为接口对应方法的返回类型\n\n 注意:1-如果方法返回的是集合,则resultType是里面元素的类型\n\n ​\t 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定\n\n\n\n```xml\n\n\n\n \n insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n \n update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id}\n \n \n delete from t_acount where id = #{id}\n \n\n \n\n \n\n\n\n```\n\n\n\n\n\n(三)注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n(四)测试\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\nimport java.util.List;\n\npublic class MyTest {\n public static void main(String[] args) {\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n// 实现接口的代理对象\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n// 增\n// accountDao.save(new Account(8, \"小叮当\", \"12345xdj\", 24));\n// sqlSession.commit(); //提交事务\n// 删\n// accountDao.deleteById(4);\n// sqlSession.commit(); //提交事务\n// 查\n// List list = accountDao.findAll();\n// for(Account account: list){\n// System.out.println(account);\n// }\n// Account account = accountDao.findById(2);\n// System.out.println(account);\n\n// 改(先查出要修改的对象,再修改对象的各个属性)\n Account account1 = accountDao.findById(1);\n account1.setUsername(\"小东\");\n account1.setAge(19);\n account1.setPasswd(\"56789xd\");\n\n int result = accountDao.update(account1);\n sqlSession.commit(); //提交事务\n System.out.println(\"修改行数:\" + result + \"修改后的对象:\");\n System.out.println(accountDao.findById(1));\n\n sqlSession.close(); //退出会话\n\n }\n}\n\n```\n\n\n\n这是进行测试的数据表\n\n\n\n![image-20210826203758449](https://gitee.com/ajream/images/raw/master/img/20210826203801_image-20210826203758449.png)\n\n\n\n\n\n","source":"_posts/Mybatis/mybatis简单使用(二).md","raw":"---\ntitle: mybatis介绍与使用(二)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: (一)的例子看不懂可以先看这个,也是一个入门mybatis的例子,该例子逻辑可能更清晰\nabbrlink: ab69c7ec\ndate: 2021-08-25 14:30:46\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n[项目地址mybatis-02](https://codechina.csdn.net/m0_46079750/mybatis-study)\n\n### 新建数据库表\n\n\n\n```sql\nUSE mybatis;\n\nCREATE TABLE t_acount(\n\tid INT PRIMARY KEY auto_increment,\n\tusername VARCHAR(11),\n\tpasswd VARCHAR(11),\n\tage INT\n)\n```\n\n\n\n### 配置mybatis开发环境\n\n\n\n使用maven创建新项目,在pom.xml导入依赖:\n\n```xml\n\n \n mysql\n mysql-connector-java\n 8.0.26\n \n\n \n org.mybatis\n mybatis\n 3.5.6\n \n\n \n junit\n junit\n 4.13\n test\n \n\n \n org.projectlombok\n lombok\n RELEASE\n compile\n \n\n```\n\n\n\n新建数据表对应的Account实体类\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n//@Data可以为类提供读写功能,从而不用写get、set方法; 会为类提供 equals()、hashCode()、toString() 方法。\n//@AllArgsConstructor 自动添加有参构造方法\n//@NoArgsConstructor 添加无参构造方法\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Account {\n private int id;\n private String username;\n private String passwd;\n private int age;\n}\n\n```\n\n\n\n在resources创建mybatis配置文件mybatis-config.xml\n\n首先要导入约束(注意这个与配置文件那个的是不同的)\n\n```xml\n\n\n```\n\nmybatis-config.xml:\n\n```xml\n\n\n\n\n\n\n \n \n\n \n\n \n\n \n \n \n \n \n\n \n \n\n```\n\n到此mybatis开发环境已经搭建好,接下来进行开发\n\n\n\n\n\n### Mybatis开发方式\n\n1. 原生接口方式\n2. mapper代理实现自定义接口方式\n\n\n\n\n\n> **先介绍第一种:原生接口方式**\n\n\n\nmybatis框架需要开发者自定义sql语句,写在Mapper.xml文件\n\n实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象\n\n\n\n\n\n(一)创建Mapper文件\n\n因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件\n\n\n\n![image-20210826131020613](https://gitee.com/ajream/images/raw/master/img/20210826131024_image-20210826131020613.png)\n\n\n\n\n\n首先也是要导入约束:\n\n```xml\n\n\n```\n\nAccountMapper.xml:\n\n```xml\n\n\n\n\n\n\n\n \n insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age})\n \n\n```\n\n说明:\n\n1. namespace通常设置为文件所在包名+文件名(无后缀)\n\n2. 可以把这个Mapper文件理解成一个java类,类名是 `AccountMapper`, 有一个方法 `save` ,可以传入参数,参数类型是 `com.ajream.entity.Account`\n\n 这个方法的作用是执行insert插入数据操作\n\n\n\n\n\n(二)注册Mapper文件\n\n在mybatis-config.xml配置文件中注册mapper文件, 添加:\n\n```xml\n\n\t\n\n```\n\n![image-20210826133054400](https://gitee.com/ajream/images/raw/master/img/20210826133057_image-20210826133054400.png)\n\n\n\n\n\n### 编写测试类进行运行\n\n1. 根据mybatis配置文件,使用工厂模式生成sqlSession\n2. 执行sql语句\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class MyTest {\n public static void main(String[] args) {\n //加载配置文件,获取sqlSession工厂对象\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis02-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n// 通过工厂对象获取sqlSession\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n String statement= \"com.ajream.mapper.AccountMapper.save\"; //用于加载mapper配置文件的save方法\n Account account = new Account(2,\"XiaoZhang\", \"56789xz\", 20);\n sqlSession.insert(statement, account); //执行sql语句\n sqlSession.commit(); //增、删、改 最后必须提交事务\n\n }\n}\n\n```\n\n\n\n\n\n\n\n注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件\n\n```xml\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\n\n\n如果报错:\n\n```\nCause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。\n```\n\n在pom.xml中添加语句:\n\n```xml\n\n UTF-8\n\n```\n\n![image-20210826150549895](https://gitee.com/ajream/images/raw/master/img/20210826150553_image-20210826150549895.png)\n\n\n\n\n\n\n\n> **第二种:Mapper代理实现自定义接口方式(推荐)**\n>\n> [项目地址mybatis-03](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master1)\n\n\n\n(一)创建接口:\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\nimport java.util.List;\n\n//增删改查\npublic interface AccountDao {\n int save(Account account);\n int update(Account account);\n int deleteById(int id);\n List findAll();\n Account findById(int id);\n}\n\n```\n\n\n\n\n\n(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过`insert/update/delete/select`标签来定义接口方法对应的sql语句\n\n\n\n规则:\n\n- namespace为接口的全类名\n\n- id为接口中对应的方法名\n\n- parameterType为接口中对应方法的参数类型\n\n- resultType为接口对应方法的返回类型\n\n 注意:1-如果方法返回的是集合,则resultType是里面元素的类型\n\n ​\t 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定\n\n\n\n```xml\n\n\n\n \n insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n \n update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id}\n \n \n delete from t_acount where id = #{id}\n \n\n \n\n \n\n\n\n```\n\n\n\n\n\n(三)注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n(四)测试\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\nimport java.util.List;\n\npublic class MyTest {\n public static void main(String[] args) {\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n// 实现接口的代理对象\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n// 增\n// accountDao.save(new Account(8, \"小叮当\", \"12345xdj\", 24));\n// sqlSession.commit(); //提交事务\n// 删\n// accountDao.deleteById(4);\n// sqlSession.commit(); //提交事务\n// 查\n// List list = accountDao.findAll();\n// for(Account account: list){\n// System.out.println(account);\n// }\n// Account account = accountDao.findById(2);\n// System.out.println(account);\n\n// 改(先查出要修改的对象,再修改对象的各个属性)\n Account account1 = accountDao.findById(1);\n account1.setUsername(\"小东\");\n account1.setAge(19);\n account1.setPasswd(\"56789xd\");\n\n int result = accountDao.update(account1);\n sqlSession.commit(); //提交事务\n System.out.println(\"修改行数:\" + result + \"修改后的对象:\");\n System.out.println(accountDao.findById(1));\n\n sqlSession.close(); //退出会话\n\n }\n}\n\n```\n\n\n\n这是进行测试的数据表\n\n\n\n![image-20210826203758449](https://gitee.com/ajream/images/raw/master/img/20210826203801_image-20210826203758449.png)\n\n\n\n\n\n","slug":"Mybatis/mybatis简单使用(二)","published":1,"updated":"2021-08-29T14:44:59.444Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pp003nakve3uzjgiks","content":"

项目地址mybatis-02

\n

新建数据库表

1
2
3
4
5
6
7
8
USE mybatis;

CREATE TABLE t_acount(
\tid INT PRIMARY KEY auto_increment,
\tusername VARCHAR(11),
\tpasswd VARCHAR(11),
\tage INT
)
\n

配置mybatis开发环境

使用maven创建新项目,在pom.xml导入依赖:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
\n

新建数据表对应的Account实体类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//@Data可以为类提供读写功能,从而不用写get、set方法; 会为类提供 equals()、hashCode()、toString() 方法。
//@AllArgsConstructor 自动添加有参构造方法
//@NoArgsConstructor 添加无参构造方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
private int id;
private String username;
private String passwd;
private int age;
}

\n

在resources创建mybatis配置文件mybatis-config.xml

\n

首先要导入约束(注意这个与配置文件那个的是不同的)

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
\n

mybatis-config.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!-- 2. 配置mybatis运行环境,可以有多个运行环境,default表示默认的运行环境是...,id是每个运行环境的唯一标识-->
<environments default="test1">
<environment id="test1">
<!-- 3. 配置JDBC事务管理-->
<transactionManager type="JDBC"></transactionManager>
<!-- 4. 配置JDBC数据源连接池POOLED-->
<dataSource type="POOLED">
<!-- 5. 配置数据库链接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--配置驱动-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>

</environment>
</environments>
</configuration>
\n

到此mybatis开发环境已经搭建好,接下来进行开发

\n

Mybatis开发方式

    \n
  1. 原生接口方式
  2. \n
  3. mapper代理实现自定义接口方式
  4. \n
\n
\n

先介绍第一种:原生接口方式

\n
\n

mybatis框架需要开发者自定义sql语句,写在Mapper.xml文件

\n

实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象

\n

(一)创建Mapper文件

\n

因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件

\n

\"image-20210826131020613\"

\n

首先也是要导入约束:

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
\n

AccountMapper.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--1. mapper标签,namespace是当前mapper文件路径-->
<mapper namespace="com.ajream.mapper.AccountMapper">
<!-- 2. 进行增删改查操作,id是后面要调用sql语句时用的的方法名,parameterType是方法中的参数的类型-->
<!-- #{id}等这些都是Account实体类中的属性,表示从中取出值-->
<insert id="save" parameterType="com.ajream.entity.Account">
insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age})
</insert>
</mapper>
\n

说明:

\n
    \n
  1. namespace通常设置为文件所在包名+文件名(无后缀)

    \n
  2. \n
  3. 可以把这个Mapper文件理解成一个java类,类名是 AccountMapper, 有一个方法 save ,可以传入参数,参数类型是 com.ajream.entity.Account

    \n

    这个方法的作用是执行insert插入数据操作

    \n
  4. \n
\n

(二)注册Mapper文件

\n

在mybatis-config.xml配置文件中注册mapper文件, 添加:

\n
1
2
3
<mappers>
\t<mapper resource="com/ajream/mapper/AccountMapper.xml"></mapper>
</mappers>
\n

\"image-20210826133054400\"

\n

编写测试类进行运行

    \n
  1. 根据mybatis配置文件,使用工厂模式生成sqlSession
  2. \n
  3. 执行sql语句
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.ajream.test;

import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class MyTest {
public static void main(String[] args) {
//加载配置文件,获取sqlSession工厂对象
InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis02-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 通过工厂对象获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

String statement= "com.ajream.mapper.AccountMapper.save"; //用于加载mapper配置文件的save方法
Account account = new Account(2,"XiaoZhang", "56789xz", 20);
sqlSession.insert(statement, account); //执行sql语句
sqlSession.commit(); //增、删、改 最后必须提交事务

}
}

\n

注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

如果报错:

\n
1
Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。
\n

在pom.xml中添加语句:

\n
1
2
3
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
\n

\"image-20210826150549895\"

\n
\n

第二种:Mapper代理实现自定义接口方式(推荐)

\n

项目地址mybatis-03

\n
\n

(一)创建接口:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.dao;

import com.ajream.entity.Account;

import java.util.List;

//增删改查
public interface AccountDao {
int save(Account account);
int update(Account account);
int deleteById(int id);
List<Account> findAll();
Account findById(int id);
}

\n

(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过insert/update/delete/select标签来定义接口方法对应的sql语句

\n

规则:

\n
    \n
  • namespace为接口的全类名

    \n
  • \n
  • id为接口中对应的方法名

    \n
  • \n
  • parameterType为接口中对应方法的参数类型

    \n
  • \n
  • resultType为接口对应方法的返回类型

    \n

    注意:1-如果方法返回的是集合,则resultType是里面元素的类型

    \n

    ​ 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ajream.dao.AccountDao">
<insert id="save" parameterType="com.ajream.entity.Account">
insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})
</insert>
<update id="update" parameterType="com.ajream.entity.Account">
update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id}
</update>
<delete id="deleteById" parameterType="int">
delete from t_acount where id = #{id}
</delete>

<select id="findAll" resultType="com.ajream.entity.Account">
select * from t_acount;
</select>

<select id="findById" resultType="com.ajream.entity.Account" parameterType="int">
select * from t_acount where id=#{id}
</select>
</mapper>


\n

(三)注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/AccountDao.xml" />
</mappers>
\n

(四)测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.ajream.test;

import com.ajream.dao.AccountDao;
import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;
import java.util.List;

public class MyTest {
public static void main(String[] args) {
InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

// 实现接口的代理对象
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

// 增
// accountDao.save(new Account(8, "小叮当", "12345xdj", 24));
// sqlSession.commit(); //提交事务
// 删
// accountDao.deleteById(4);
// sqlSession.commit(); //提交事务
// 查
// List<Account> list = accountDao.findAll();
// for(Account account: list){
// System.out.println(account);
// }
// Account account = accountDao.findById(2);
// System.out.println(account);

// 改(先查出要修改的对象,再修改对象的各个属性)
Account account1 = accountDao.findById(1);
account1.setUsername("小东");
account1.setAge(19);
account1.setPasswd("56789xd");

int result = accountDao.update(account1);
sqlSession.commit(); //提交事务
System.out.println("修改行数:" + result + "修改后的对象:");
System.out.println(accountDao.findById(1));

sqlSession.close(); //退出会话

}
}

\n

这是进行测试的数据表

\n

\"image-20210826203758449\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

项目地址mybatis-02

\n

新建数据库表

1
2
3
4
5
6
7
8
USE mybatis;

CREATE TABLE t_acount(
\tid INT PRIMARY KEY auto_increment,
\tusername VARCHAR(11),
\tpasswd VARCHAR(11),
\tage INT
)
\n

配置mybatis开发环境

使用maven创建新项目,在pom.xml导入依赖:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
\n

新建数据表对应的Account实体类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//@Data可以为类提供读写功能,从而不用写get、set方法; 会为类提供 equals()、hashCode()、toString() 方法。
//@AllArgsConstructor 自动添加有参构造方法
//@NoArgsConstructor 添加无参构造方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
private int id;
private String username;
private String passwd;
private int age;
}

\n

在resources创建mybatis配置文件mybatis-config.xml

\n

首先要导入约束(注意这个与配置文件那个的是不同的)

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
\n

mybatis-config.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!-- 2. 配置mybatis运行环境,可以有多个运行环境,default表示默认的运行环境是...,id是每个运行环境的唯一标识-->
<environments default="test1">
<environment id="test1">
<!-- 3. 配置JDBC事务管理-->
<transactionManager type="JDBC"></transactionManager>
<!-- 4. 配置JDBC数据源连接池POOLED-->
<dataSource type="POOLED">
<!-- 5. 配置数据库链接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--配置驱动-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>

</environment>
</environments>
</configuration>
\n

到此mybatis开发环境已经搭建好,接下来进行开发

\n

Mybatis开发方式

    \n
  1. 原生接口方式
  2. \n
  3. mapper代理实现自定义接口方式
  4. \n
\n
\n

先介绍第一种:原生接口方式

\n
\n

mybatis框架需要开发者自定义sql语句,写在Mapper.xml文件

\n

实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象

\n

(一)创建Mapper文件

\n

因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件

\n

\"image-20210826131020613\"

\n

首先也是要导入约束:

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
\n

AccountMapper.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--1. mapper标签,namespace是当前mapper文件路径-->
<mapper namespace="com.ajream.mapper.AccountMapper">
<!-- 2. 进行增删改查操作,id是后面要调用sql语句时用的的方法名,parameterType是方法中的参数的类型-->
<!-- #{id}等这些都是Account实体类中的属性,表示从中取出值-->
<insert id="save" parameterType="com.ajream.entity.Account">
insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age})
</insert>
</mapper>
\n

说明:

\n
    \n
  1. namespace通常设置为文件所在包名+文件名(无后缀)

    \n
  2. \n
  3. 可以把这个Mapper文件理解成一个java类,类名是 AccountMapper, 有一个方法 save ,可以传入参数,参数类型是 com.ajream.entity.Account

    \n

    这个方法的作用是执行insert插入数据操作

    \n
  4. \n
\n

(二)注册Mapper文件

\n

在mybatis-config.xml配置文件中注册mapper文件, 添加:

\n
1
2
3
<mappers>
\t<mapper resource="com/ajream/mapper/AccountMapper.xml"></mapper>
</mappers>
\n

\"image-20210826133054400\"

\n

编写测试类进行运行

    \n
  1. 根据mybatis配置文件,使用工厂模式生成sqlSession
  2. \n
  3. 执行sql语句
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.ajream.test;

import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class MyTest {
public static void main(String[] args) {
//加载配置文件,获取sqlSession工厂对象
InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis02-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 通过工厂对象获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

String statement= "com.ajream.mapper.AccountMapper.save"; //用于加载mapper配置文件的save方法
Account account = new Account(2,"XiaoZhang", "56789xz", 20);
sqlSession.insert(statement, account); //执行sql语句
sqlSession.commit(); //增、删、改 最后必须提交事务

}
}

\n

注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

如果报错:

\n
1
Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。
\n

在pom.xml中添加语句:

\n
1
2
3
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
\n

\"image-20210826150549895\"

\n
\n

第二种:Mapper代理实现自定义接口方式(推荐)

\n

项目地址mybatis-03

\n
\n

(一)创建接口:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.dao;

import com.ajream.entity.Account;

import java.util.List;

//增删改查
public interface AccountDao {
int save(Account account);
int update(Account account);
int deleteById(int id);
List<Account> findAll();
Account findById(int id);
}

\n

(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过insert/update/delete/select标签来定义接口方法对应的sql语句

\n

规则:

\n
    \n
  • namespace为接口的全类名

    \n
  • \n
  • id为接口中对应的方法名

    \n
  • \n
  • parameterType为接口中对应方法的参数类型

    \n
  • \n
  • resultType为接口对应方法的返回类型

    \n

    注意:1-如果方法返回的是集合,则resultType是里面元素的类型

    \n

    ​ 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ajream.dao.AccountDao">
<insert id="save" parameterType="com.ajream.entity.Account">
insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})
</insert>
<update id="update" parameterType="com.ajream.entity.Account">
update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id}
</update>
<delete id="deleteById" parameterType="int">
delete from t_acount where id = #{id}
</delete>

<select id="findAll" resultType="com.ajream.entity.Account">
select * from t_acount;
</select>

<select id="findById" resultType="com.ajream.entity.Account" parameterType="int">
select * from t_acount where id=#{id}
</select>
</mapper>


\n

(三)注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/AccountDao.xml" />
</mappers>
\n

(四)测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.ajream.test;

import com.ajream.dao.AccountDao;
import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;
import java.util.List;

public class MyTest {
public static void main(String[] args) {
InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

// 实现接口的代理对象
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

// 增
// accountDao.save(new Account(8, "小叮当", "12345xdj", 24));
// sqlSession.commit(); //提交事务
// 删
// accountDao.deleteById(4);
// sqlSession.commit(); //提交事务
// 查
// List<Account> list = accountDao.findAll();
// for(Account account: list){
// System.out.println(account);
// }
// Account account = accountDao.findById(2);
// System.out.println(account);

// 改(先查出要修改的对象,再修改对象的各个属性)
Account account1 = accountDao.findById(1);
account1.setUsername("小东");
account1.setAge(19);
account1.setPasswd("56789xd");

int result = accountDao.update(account1);
sqlSession.commit(); //提交事务
System.out.println("修改行数:" + result + "修改后的对象:");
System.out.println(accountDao.findById(1));

sqlSession.close(); //退出会话

}
}

\n

这是进行测试的数据表

\n

\"image-20210826203758449\"

\n"},{"title":"mybatis逆向工程(五)","description":"mybatis框架需要:实体类、自定义mapper接口、mapper.xml,传统开发需要手动创建,而mybatis可以用逆向工程MBG来自动帮助我们创建这3个组件,提高开发效率","abbrlink":"81ecdf4a","date":"2021-08-27T02:22:46.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n\n\nmybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml\n\n传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率\n\n\n\n\n\n如何使用?\n\n\n\n\n\n(1) 需要额外添加依赖\n\n```xml\n\n org.mybatis.generator\n mybatis-generator-maven-plugin\n 1.3.2\n\n```\n\n\n\n\n\n(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)\n\n\n\n需要配置的信息:\n\n1. jdbcConnection:数据库连接信息\n2. JavaModelGenerator:javaBean(实体类)生成策略\n3. sqlMapGenerator:sql映射文件生成策略\n4. javaClientGenerator:配置Mapper接口生成策略\n5. table :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名)\n\n\n\n首先添加约束\n\n```xml\n\n\n```\n\nGeneratorConfig.xml\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n(3)创建Generator执行类\n\n\n\n```java\npackage com.ajream.test;\n\nimport org.mybatis.generator.api.MyBatisGenerator;\nimport org.mybatis.generator.config.Configuration;\nimport org.mybatis.generator.config.xml.ConfigurationParser;\nimport org.mybatis.generator.exception.InvalidConfigurationException;\nimport org.mybatis.generator.exception.XMLParserException;\nimport org.mybatis.generator.internal.DefaultShellCallback;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n public static void main(String[] args) {\n List warings = new ArrayList();\n boolean overwrite = true;\n String genCig = \"/GeneratorConfig.xml\";\n File configFile = new File(Main.class.getResource(genCig).getFile());\n// File configFile = new File(\"GeneratorConfig.xml\");\n ConfigurationParser configurationParser=new ConfigurationParser(warings);\n Configuration configuration = null;\n try{\n configuration = configurationParser.parseConfiguration(configFile);\n } catch (XMLParserException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n }\n DefaultShellCallback callback = new DefaultShellCallback(overwrite);\n MyBatisGenerator myBatisGenerator = null;\n try{\n myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);\n } catch (InvalidConfigurationException e) {\n e.printStackTrace();\n }\n\n try{\n myBatisGenerator.generate(null);\n } catch (InterruptedException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n } catch (SQLException throwables) {\n throwables.printStackTrace();\n }\n\n System.out.println(\"ok...\");\n }\n}\n\n```\n\n\n\n运行即可在指定路径下生成相应组件\n","source":"_posts/Mybatis/mybatis逆向工程(五).md","raw":"---\ntitle: mybatis逆向工程(五)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: >-\n mybatis框架需要:实体类、自定义mapper接口、mapper.xml,传统开发需要手动创建,而mybatis可以用逆向工程MBG来自动帮助我们创建这3个组件,提高开发效率\nabbrlink: 81ecdf4a\ndate: 2021-08-27 10:22:46\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n\n\nmybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml\n\n传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率\n\n\n\n\n\n如何使用?\n\n\n\n\n\n(1) 需要额外添加依赖\n\n```xml\n\n org.mybatis.generator\n mybatis-generator-maven-plugin\n 1.3.2\n\n```\n\n\n\n\n\n(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)\n\n\n\n需要配置的信息:\n\n1. jdbcConnection:数据库连接信息\n2. JavaModelGenerator:javaBean(实体类)生成策略\n3. sqlMapGenerator:sql映射文件生成策略\n4. javaClientGenerator:配置Mapper接口生成策略\n5. table :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名)\n\n\n\n首先添加约束\n\n```xml\n\n\n```\n\nGeneratorConfig.xml\n\n```xml\n\n\n\n\n \n \n \n \n \n \n
\n \n\n```\n\n\n\n(3)创建Generator执行类\n\n\n\n```java\npackage com.ajream.test;\n\nimport org.mybatis.generator.api.MyBatisGenerator;\nimport org.mybatis.generator.config.Configuration;\nimport org.mybatis.generator.config.xml.ConfigurationParser;\nimport org.mybatis.generator.exception.InvalidConfigurationException;\nimport org.mybatis.generator.exception.XMLParserException;\nimport org.mybatis.generator.internal.DefaultShellCallback;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n public static void main(String[] args) {\n List warings = new ArrayList();\n boolean overwrite = true;\n String genCig = \"/GeneratorConfig.xml\";\n File configFile = new File(Main.class.getResource(genCig).getFile());\n// File configFile = new File(\"GeneratorConfig.xml\");\n ConfigurationParser configurationParser=new ConfigurationParser(warings);\n Configuration configuration = null;\n try{\n configuration = configurationParser.parseConfiguration(configFile);\n } catch (XMLParserException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n }\n DefaultShellCallback callback = new DefaultShellCallback(overwrite);\n MyBatisGenerator myBatisGenerator = null;\n try{\n myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);\n } catch (InvalidConfigurationException e) {\n e.printStackTrace();\n }\n\n try{\n myBatisGenerator.generate(null);\n } catch (InterruptedException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n } catch (SQLException throwables) {\n throwables.printStackTrace();\n }\n\n System.out.println(\"ok...\");\n }\n}\n\n```\n\n\n\n运行即可在指定路径下生成相应组件\n","slug":"Mybatis/mybatis逆向工程(五)","published":1,"updated":"2021-08-29T14:45:15.143Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pq003pakveexb0ak5a","content":"

mybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml

\n

传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率

\n

如何使用?

\n

(1) 需要额外添加依赖

\n
1
2
3
4
5
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
</dependency>
\n

(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)

\n

需要配置的信息:

\n
    \n
  1. jdbcConnection:数据库连接信息
  2. \n
  3. JavaModelGenerator:javaBean(实体类)生成策略
  4. \n
  5. sqlMapGenerator:sql映射文件生成策略
  6. \n
  7. javaClientGenerator:配置Mapper接口生成策略
  8. \n
  9. table :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名)
  10. \n
\n

首先添加约束

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
\n

GeneratorConfig.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3"> <!--运行环境必须是MyBatis3-->
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"
userId="root"
password="admin"
/>
<!--targetPackage表示实体类存放在哪个包,targetProject表示包com.ajream.entity存放的路径-->
<javaModelGenerator targetPackage="com.ajream.entity" targetProject="./src/main/java"/>
<sqlMapGenerator targetPackage="com.ajream.dao" targetProject="./src/main/java"/>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.ajream.entity" targetProject="./src/main/java"/>
<table tableName="t_acount" domainObjectName="Account"/>
</context>
</generatorConfiguration>
\n

(3)创建Generator执行类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.ajream.test;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class Main {
public static void main(String[] args) {
List<String> warings = new ArrayList<String>();
boolean overwrite = true;
String genCig = "/GeneratorConfig.xml";
File configFile = new File(Main.class.getResource(genCig).getFile());
// File configFile = new File("GeneratorConfig.xml");
ConfigurationParser configurationParser=new ConfigurationParser(warings);
Configuration configuration = null;
try{
configuration = configurationParser.parseConfiguration(configFile);
} catch (XMLParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = null;
try{
myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);
} catch (InvalidConfigurationException e) {
e.printStackTrace();
}

try{
myBatisGenerator.generate(null);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}

System.out.println("ok...");
}
}

\n

运行即可在指定路径下生成相应组件

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

mybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml

\n

传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率

\n

如何使用?

\n

(1) 需要额外添加依赖

\n
1
2
3
4
5
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
</dependency>
\n

(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)

\n

需要配置的信息:

\n
    \n
  1. jdbcConnection:数据库连接信息
  2. \n
  3. JavaModelGenerator:javaBean(实体类)生成策略
  4. \n
  5. sqlMapGenerator:sql映射文件生成策略
  6. \n
  7. javaClientGenerator:配置Mapper接口生成策略
  8. \n
  9. table :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名)
  10. \n
\n

首先添加约束

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
\n

GeneratorConfig.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3"> <!--运行环境必须是MyBatis3-->
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"
userId="root"
password="admin"
/>
<!--targetPackage表示实体类存放在哪个包,targetProject表示包com.ajream.entity存放的路径-->
<javaModelGenerator targetPackage="com.ajream.entity" targetProject="./src/main/java"/>
<sqlMapGenerator targetPackage="com.ajream.dao" targetProject="./src/main/java"/>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.ajream.entity" targetProject="./src/main/java"/>
<table tableName="t_acount" domainObjectName="Account"/>
</context>
</generatorConfiguration>
\n

(3)创建Generator执行类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.ajream.test;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class Main {
public static void main(String[] args) {
List<String> warings = new ArrayList<String>();
boolean overwrite = true;
String genCig = "/GeneratorConfig.xml";
File configFile = new File(Main.class.getResource(genCig).getFile());
// File configFile = new File("GeneratorConfig.xml");
ConfigurationParser configurationParser=new ConfigurationParser(warings);
Configuration configuration = null;
try{
configuration = configurationParser.parseConfiguration(configFile);
} catch (XMLParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = null;
try{
myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);
} catch (InvalidConfigurationException e) {
e.printStackTrace();
}

try{
myBatisGenerator.generate(null);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}

System.out.println("ok...");
}
}

\n

运行即可在指定路径下生成相应组件

\n"},{"title":"esp8266(1)-接入点模式-无线终端模式","abbrlink":"4dc0a7a8","date":"2021-08-02T00:00:00.000Z","description":"esp8266如何开启热点(接入点模式)、如何连接WiFi(无线终端模式)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n\n接入点模式:开发板作为路由器(网关),可以让其他设备连接\n\n无线终端模式:开发板作为终端,可以连接路由器WiFi\n\n\n\n> 说明:此处使用ArduinoIDE来进行开发。\n>\n> 注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了\n\n\n\n![ESP8266-NodeMCU引脚功能](http://www.taichi-maker.com/wp-content/uploads/2019/02/esp8266_devkit_horizontal-001.png)\n\n\n\n## 1、配置接入点模式\n\n即开启 “热点”\n\n1. 导入 `ESP8266WiFi.h`库\n\n2. 配置WiFi名称(ssid)、密码(passwd)—— `softAP()`\n\n ```c\n const char ssid[] = \"MyWiFi\";\n const char passwd[] = \"12345678\";\n WiFi.softAP(ssid, passwd);\n ```\n\n3. 获取开发板IP\n\n ```c\n WiFi.softAPIP();\n ```\n\n\n\n完整code\n\n```c\n#include \n\nchar ssid[] = \"MyWiFi\";\nchar passwd[] = \"1234567890\";\n\nvoid setup(){\n Serial.begin(9600);\n \n Serial.print(\"wifi ssid: \");\n Serial.println(ssid);\n Serial.print(\"wifi password: \");\n Serial.println(passwd);\n \n WiFi.softAP(ssid, passwd);\t//开启接入点模式\n\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.softAPIP());\t//打印开发板的IP\n}\n\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n## 2、配置无线终端模式\n\n### 只有1个WiFi\n\n将开发板连接WiFi\n\n```c\n//ssid、passwd均为字符串\nWiFi.mode(WIFI_STA);\nWiFi.begin(ssid, passwd);\n```\n\n\n\n多次尝试连接,直到连接上:\n\n```c\nwhile(WiFi.status() != WL_CONNECTED\t){\n delay(1000);\n //....\n}\n```\n\n\n\n连上WiFi后,获取wifi的ssid、开发板IP地址:\n\n```c\nWiFi.SSID();\t\t//返回字符串\nWiFi.localIP(); //返回字符串\n```\n\n\n\n完整code:\n\n```c\n#include // 本程序使用ESP8266WiFi库\n\nString ssid = \"aaa\"; \nString password = \"12345678\"; \n \n \nvoid setup() {\n Serial.begin(9600); \n WiFi.begin(ssid, password); \n \n int i = 0; \n while (WiFi.status() != WL_CONNECTED) { \n delay(1000); \n Serial.print(i++); Serial.print(' '); \n } \n \n Serial.println(\"\"); \n Serial.print(\"Connecting to \"); \n Serial.println(ssid); \n \n Serial.println(\"IP address: \" ); \n Serial.println(WiFi.localIP()); \n}\n\nvoid loop() { \n}\n```\n\n\n\n### 多个WiFi,自动连接最强信号的\n\n1. 先添加几个待选WiFi\n\n ```c\n //导入库\n #include \n \n // 创建ESP8266WiFiMulti对象\n ESP8266WiFiMulti wifiMulti; \n \n //通过addAP()函数存储WiFi的ssid和密码\n wifiMulti.addAP(\"aaaa\", \"11111111\");\n wifiMulti.addAP(\"bbbb\", \"22222222\");\n wifiMulti.addAP(\"cccc\", \"33333333\");\n \n ```\n\n \n\n2. 多次尝试连接:`wifiMulti.run()`\n\n ```c\n // 将会连接信号最强的那一个WiFi信号。\n while (wifiMulti.run() != WL_CONNECTED) { \n delay(1000); \n Serial.print('...'); \n } \n ```\n\n \n\n\n\n完整code:\n\n```c\n#include \n#include \n\nESP8266WiFiMulti wifiMulti; //创建对象\n\nvoid setup(){\n Serial.begin(9600);\n wifiMulti.addAP(\"aaa\", \"12345678\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(1000);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\nvoid loop(){\n\n}\n```\n\n","source":"_posts/esp8266/1-接入点模式-无线终端模式.md","raw":"---\ntitle: esp8266(1)-接入点模式-无线终端模式\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 4dc0a7a8\ndate: 2021-08-02 08:00:00\ndescription: esp8266如何开启热点(接入点模式)、如何连接WiFi(无线终端模式)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n\n接入点模式:开发板作为路由器(网关),可以让其他设备连接\n\n无线终端模式:开发板作为终端,可以连接路由器WiFi\n\n\n\n> 说明:此处使用ArduinoIDE来进行开发。\n>\n> 注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了\n\n\n\n![ESP8266-NodeMCU引脚功能](http://www.taichi-maker.com/wp-content/uploads/2019/02/esp8266_devkit_horizontal-001.png)\n\n\n\n## 1、配置接入点模式\n\n即开启 “热点”\n\n1. 导入 `ESP8266WiFi.h`库\n\n2. 配置WiFi名称(ssid)、密码(passwd)—— `softAP()`\n\n ```c\n const char ssid[] = \"MyWiFi\";\n const char passwd[] = \"12345678\";\n WiFi.softAP(ssid, passwd);\n ```\n\n3. 获取开发板IP\n\n ```c\n WiFi.softAPIP();\n ```\n\n\n\n完整code\n\n```c\n#include \n\nchar ssid[] = \"MyWiFi\";\nchar passwd[] = \"1234567890\";\n\nvoid setup(){\n Serial.begin(9600);\n \n Serial.print(\"wifi ssid: \");\n Serial.println(ssid);\n Serial.print(\"wifi password: \");\n Serial.println(passwd);\n \n WiFi.softAP(ssid, passwd);\t//开启接入点模式\n\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.softAPIP());\t//打印开发板的IP\n}\n\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n## 2、配置无线终端模式\n\n### 只有1个WiFi\n\n将开发板连接WiFi\n\n```c\n//ssid、passwd均为字符串\nWiFi.mode(WIFI_STA);\nWiFi.begin(ssid, passwd);\n```\n\n\n\n多次尝试连接,直到连接上:\n\n```c\nwhile(WiFi.status() != WL_CONNECTED\t){\n delay(1000);\n //....\n}\n```\n\n\n\n连上WiFi后,获取wifi的ssid、开发板IP地址:\n\n```c\nWiFi.SSID();\t\t//返回字符串\nWiFi.localIP(); //返回字符串\n```\n\n\n\n完整code:\n\n```c\n#include // 本程序使用ESP8266WiFi库\n\nString ssid = \"aaa\"; \nString password = \"12345678\"; \n \n \nvoid setup() {\n Serial.begin(9600); \n WiFi.begin(ssid, password); \n \n int i = 0; \n while (WiFi.status() != WL_CONNECTED) { \n delay(1000); \n Serial.print(i++); Serial.print(' '); \n } \n \n Serial.println(\"\"); \n Serial.print(\"Connecting to \"); \n Serial.println(ssid); \n \n Serial.println(\"IP address: \" ); \n Serial.println(WiFi.localIP()); \n}\n\nvoid loop() { \n}\n```\n\n\n\n### 多个WiFi,自动连接最强信号的\n\n1. 先添加几个待选WiFi\n\n ```c\n //导入库\n #include \n \n // 创建ESP8266WiFiMulti对象\n ESP8266WiFiMulti wifiMulti; \n \n //通过addAP()函数存储WiFi的ssid和密码\n wifiMulti.addAP(\"aaaa\", \"11111111\");\n wifiMulti.addAP(\"bbbb\", \"22222222\");\n wifiMulti.addAP(\"cccc\", \"33333333\");\n \n ```\n\n \n\n2. 多次尝试连接:`wifiMulti.run()`\n\n ```c\n // 将会连接信号最强的那一个WiFi信号。\n while (wifiMulti.run() != WL_CONNECTED) { \n delay(1000); \n Serial.print('...'); \n } \n ```\n\n \n\n\n\n完整code:\n\n```c\n#include \n#include \n\nESP8266WiFiMulti wifiMulti; //创建对象\n\nvoid setup(){\n Serial.begin(9600);\n wifiMulti.addAP(\"aaa\", \"12345678\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(1000);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\nvoid loop(){\n\n}\n```\n\n","slug":"esp8266/1-接入点模式-无线终端模式","published":1,"updated":"2021-09-10T03:19:46.998Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pr003sakve2exe8imp","content":"

接入点模式:开发板作为路由器(网关),可以让其他设备连接

\n

无线终端模式:开发板作为终端,可以连接路由器WiFi

\n
\n

说明:此处使用ArduinoIDE来进行开发。

\n

注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了

\n
\n

\"ESP8266-NodeMCU引脚功能\"

\n

1、配置接入点模式

即开启 “热点”

\n
    \n
  1. 导入 ESP8266WiFi.h

    \n
  2. \n
  3. 配置WiFi名称(ssid)、密码(passwd)—— softAP()

    \n
    1
    2
    3
    const char ssid[] = "MyWiFi";
    const char passwd[] = "12345678";
    WiFi.softAP(ssid, passwd);
    \n
  4. \n
  5. 获取开发板IP

    \n
    1
    WiFi.softAPIP();
    \n
  6. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <ESP8266WiFi.h>

char ssid[] = "MyWiFi";
char passwd[] = "1234567890";

void setup(){
Serial.begin(9600);

Serial.print("wifi ssid: ");
Serial.println(ssid);
Serial.print("wifi password: ");
Serial.println(passwd);

WiFi.softAP(ssid, passwd);\t//开启接入点模式

Serial.print("IP Address: ");
Serial.println(WiFi.softAPIP());\t//打印开发板的IP
}


void loop(){

}
\n

2、配置无线终端模式

只有1个WiFi

将开发板连接WiFi

\n
1
2
3
//ssid、passwd均为字符串
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passwd);
\n

多次尝试连接,直到连接上:

\n
1
2
3
4
while(WiFi.status() != WL_CONNECTED\t){
delay(1000);
//....
}
\n

连上WiFi后,获取wifi的ssid、开发板IP地址:

\n
1
2
WiFi.SSID();\t\t//返回字符串
WiFi.localIP(); //返回字符串
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <ESP8266WiFi.h>        // 本程序使用ESP8266WiFi库

String ssid = "aaa";
String password = "12345678";


void setup() {
Serial.begin(9600);
WiFi.begin(ssid, password);

int i = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(i++); Serial.print(' ');
}

Serial.println("");
Serial.print("Connecting to ");
Serial.println(ssid);

Serial.println("IP address: " );
Serial.println(WiFi.localIP());
}

void loop() {
}
\n

多个WiFi,自动连接最强信号的

    \n
  1. 先添加几个待选WiFi

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //导入库
    #include <ESP8266WiFiMulti.h>

    // 创建ESP8266WiFiMulti对象
    ESP8266WiFiMulti wifiMulti;

    //通过addAP()函数存储WiFi的ssid和密码
    wifiMulti.addAP("aaaa", "11111111");
    wifiMulti.addAP("bbbb", "22222222");
    wifiMulti.addAP("cccc", "33333333");

    \n
  2. \n
\n
    \n
  1. 多次尝试连接:wifiMulti.run()

    \n
    1
    2
    3
    4
    5
     // 将会连接信号最强的那一个WiFi信号。
    while (wifiMulti.run() != WL_CONNECTED) {
    delay(1000);
    Serial.print('...');
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

ESP8266WiFiMulti wifiMulti; //创建对象

void setup(){
Serial.begin(9600);
wifiMulti.addAP("aaa", "12345678");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(1000);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());

}

void loop(){

}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

接入点模式:开发板作为路由器(网关),可以让其他设备连接

\n

无线终端模式:开发板作为终端,可以连接路由器WiFi

\n
\n

说明:此处使用ArduinoIDE来进行开发。

\n

注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了

\n
\n

\"ESP8266-NodeMCU引脚功能\"

\n

1、配置接入点模式

即开启 “热点”

\n
    \n
  1. 导入 ESP8266WiFi.h

    \n
  2. \n
  3. 配置WiFi名称(ssid)、密码(passwd)—— softAP()

    \n
    1
    2
    3
    const char ssid[] = "MyWiFi";
    const char passwd[] = "12345678";
    WiFi.softAP(ssid, passwd);
    \n
  4. \n
  5. 获取开发板IP

    \n
    1
    WiFi.softAPIP();
    \n
  6. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <ESP8266WiFi.h>

char ssid[] = "MyWiFi";
char passwd[] = "1234567890";

void setup(){
Serial.begin(9600);

Serial.print("wifi ssid: ");
Serial.println(ssid);
Serial.print("wifi password: ");
Serial.println(passwd);

WiFi.softAP(ssid, passwd);\t//开启接入点模式

Serial.print("IP Address: ");
Serial.println(WiFi.softAPIP());\t//打印开发板的IP
}


void loop(){

}
\n

2、配置无线终端模式

只有1个WiFi

将开发板连接WiFi

\n
1
2
3
//ssid、passwd均为字符串
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passwd);
\n

多次尝试连接,直到连接上:

\n
1
2
3
4
while(WiFi.status() != WL_CONNECTED\t){
delay(1000);
//....
}
\n

连上WiFi后,获取wifi的ssid、开发板IP地址:

\n
1
2
WiFi.SSID();\t\t//返回字符串
WiFi.localIP(); //返回字符串
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <ESP8266WiFi.h>        // 本程序使用ESP8266WiFi库

String ssid = "aaa";
String password = "12345678";


void setup() {
Serial.begin(9600);
WiFi.begin(ssid, password);

int i = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(i++); Serial.print(' ');
}

Serial.println("");
Serial.print("Connecting to ");
Serial.println(ssid);

Serial.println("IP address: " );
Serial.println(WiFi.localIP());
}

void loop() {
}
\n

多个WiFi,自动连接最强信号的

    \n
  1. 先添加几个待选WiFi

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //导入库
    #include <ESP8266WiFiMulti.h>

    // 创建ESP8266WiFiMulti对象
    ESP8266WiFiMulti wifiMulti;

    //通过addAP()函数存储WiFi的ssid和密码
    wifiMulti.addAP("aaaa", "11111111");
    wifiMulti.addAP("bbbb", "22222222");
    wifiMulti.addAP("cccc", "33333333");

    \n
  2. \n
\n
    \n
  1. 多次尝试连接:wifiMulti.run()

    \n
    1
    2
    3
    4
    5
     // 将会连接信号最强的那一个WiFi信号。
    while (wifiMulti.run() != WL_CONNECTED) {
    delay(1000);
    Serial.print('...');
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

ESP8266WiFiMulti wifiMulti; //创建对象

void setup(){
Serial.begin(9600);
wifiMulti.addAP("aaa", "12345678");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(1000);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());

}

void loop(){

}
\n"},{"title":"esp8266(0)-Arduino配置esp8266开发环境","description":"如何用Arduino配置esp8266开发环境","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","abbrlink":"89cf4bca","date":"2021-08-01T00:10:21.000Z","_content":"\n\n\n\n\n### 一般方法(要求网速较好)\n\n\n\n1. 打开ArduinoIDE,在 **文件->首选项** 的附加开发板管理器网址 添加下面地址:\n\n ```http\n http://arduino.esp8266.com/stable/package_esp8266com_index.json\n ```\n\n ![image-20210807195949825](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-16-02_image-20210807195949825.png)\n\n2. 在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可\n\n ![image-20210807201352339](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-13-53_image-20210807201352339.png)\n\n\n\n\n\n### 特殊方法(适用于网络不好)\n\n一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可\n\n**过程较为麻烦,不想看的直接看最后:**\n\n1. 去[earlephilhower](https://github.com/earlephilhower/esp-quick-toolchain/releases/)下载下面这4个zip文件:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip\n python3-3.7.2.post1-embed-win32v2a.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip\n ```\n\n2. 去[esp8266-Arduino](https://github.com/esp8266/Arduino/releases/tag/3.0.2)下载esp8266-3.0.2.zip\n\n ![image-20210807202721141](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-27-22_image-20210807202721141.png)\n\n3. 克隆这个仓库,或者下载zip包\n\n ```git\n https://github.com/esp8266/Arduino.git\n ```\n\n 解压后改名为esp8266,放到目录(没有的文件夹自己新建):\n\n ```\n C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n ```\n\n4. 用第2步下载的zip包中的 `libraries` 文件夹来代替第3步下载的文件夹中 的`libraries`\n\n5. 将第一步下载的4个zip包分别重命名:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip --> xtensa-lx106-elf.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip\n python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip\n ```\n\n 然后解压到在第三步的 `tool`文件夹中,下面是解压后:\n\n ![image-20210807203943403](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-39-44_image-20210807203943403.png)\n\n\n\n\n\n> 我已经把这5步全部做完,放到压缩包 `esp8266.zip` 中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:\n>\n> ```\n> C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n> ```\n\n\n\n","source":"_posts/esp8266/0-Arduino配置esp8266开发环境.md","raw":"---\ntitle: esp8266(0)-Arduino配置esp8266开发环境\ntags:\n - esp8266\ncategories:\n - 硬件学习\n - esp8266\ndescription: 如何用Arduino配置esp8266开发环境\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png'\nabbrlink: 89cf4bca\ndate: 2021-08-01 08:10:21\n---\n\n\n\n\n\n### 一般方法(要求网速较好)\n\n\n\n1. 打开ArduinoIDE,在 **文件->首选项** 的附加开发板管理器网址 添加下面地址:\n\n ```http\n http://arduino.esp8266.com/stable/package_esp8266com_index.json\n ```\n\n ![image-20210807195949825](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-16-02_image-20210807195949825.png)\n\n2. 在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可\n\n ![image-20210807201352339](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-13-53_image-20210807201352339.png)\n\n\n\n\n\n### 特殊方法(适用于网络不好)\n\n一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可\n\n**过程较为麻烦,不想看的直接看最后:**\n\n1. 去[earlephilhower](https://github.com/earlephilhower/esp-quick-toolchain/releases/)下载下面这4个zip文件:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip\n python3-3.7.2.post1-embed-win32v2a.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip\n ```\n\n2. 去[esp8266-Arduino](https://github.com/esp8266/Arduino/releases/tag/3.0.2)下载esp8266-3.0.2.zip\n\n ![image-20210807202721141](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-27-22_image-20210807202721141.png)\n\n3. 克隆这个仓库,或者下载zip包\n\n ```git\n https://github.com/esp8266/Arduino.git\n ```\n\n 解压后改名为esp8266,放到目录(没有的文件夹自己新建):\n\n ```\n C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n ```\n\n4. 用第2步下载的zip包中的 `libraries` 文件夹来代替第3步下载的文件夹中 的`libraries`\n\n5. 将第一步下载的4个zip包分别重命名:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip --> xtensa-lx106-elf.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip\n python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip\n ```\n\n 然后解压到在第三步的 `tool`文件夹中,下面是解压后:\n\n ![image-20210807203943403](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-39-44_image-20210807203943403.png)\n\n\n\n\n\n> 我已经把这5步全部做完,放到压缩包 `esp8266.zip` 中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:\n>\n> ```\n> C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n> ```\n\n\n\n","slug":"esp8266/0-Arduino配置esp8266开发环境","published":1,"updated":"2021-09-11T07:55:32.130Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ps003uakve66vo01gc","content":"\n

一般方法(要求网速较好)

    \n
  1. 打开ArduinoIDE,在 文件->首选项附加开发板管理器网址 添加下面地址:

    \n
    1
    http://arduino.esp8266.com/stable/package_esp8266com_index.json
    \n

    \"image-20210807195949825\"

    \n
  2. \n
  3. 在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可

    \n

    \"image-20210807201352339\"

    \n
  4. \n
\n

特殊方法(适用于网络不好)

一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可

\n

过程较为麻烦,不想看的直接看最后:

\n
    \n
  1. earlephilhower下载下面这4个zip文件:

    \n
    1
    2
    3
    4
    x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip
    x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip
    python3-3.7.2.post1-embed-win32v2a.zip
    x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip
    \n
  2. \n
  3. esp8266-Arduino下载esp8266-3.0.2.zip

    \n

    \"image-20210807202721141\"

    \n
  4. \n
  5. 克隆这个仓库,或者下载zip包

    \n
    1
    https://github.com/esp8266/Arduino.git
    \n

    解压后改名为esp8266,放到目录(没有的文件夹自己新建):

    \n
    1
    C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
    \n
  6. \n
  7. 用第2步下载的zip包中的 libraries 文件夹来代替第3步下载的文件夹中 的libraries

    \n
  8. \n
  9. 将第一步下载的4个zip包分别重命名:

    \n
    1
    2
    3
    4
    x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip  -->  xtensa-lx106-elf.zip
    x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip
    python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip
    x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip
    \n

    然后解压到在第三步的 tool文件夹中,下面是解压后:

    \n

    \"image-20210807203943403\"

    \n
  10. \n
\n
\n

我已经把这5步全部做完,放到压缩包 esp8266.zip 中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:

\n
1
C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"\n

一般方法(要求网速较好)

    \n
  1. 打开ArduinoIDE,在 文件->首选项附加开发板管理器网址 添加下面地址:

    \n
    1
    http://arduino.esp8266.com/stable/package_esp8266com_index.json
    \n

    \"image-20210807195949825\"

    \n
  2. \n
  3. 在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可

    \n

    \"image-20210807201352339\"

    \n
  4. \n
\n

特殊方法(适用于网络不好)

一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可

\n

过程较为麻烦,不想看的直接看最后:

\n
    \n
  1. earlephilhower下载下面这4个zip文件:

    \n
    1
    2
    3
    4
    x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip
    x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip
    python3-3.7.2.post1-embed-win32v2a.zip
    x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip
    \n
  2. \n
  3. esp8266-Arduino下载esp8266-3.0.2.zip

    \n

    \"image-20210807202721141\"

    \n
  4. \n
  5. 克隆这个仓库,或者下载zip包

    \n
    1
    https://github.com/esp8266/Arduino.git
    \n

    解压后改名为esp8266,放到目录(没有的文件夹自己新建):

    \n
    1
    C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
    \n
  6. \n
  7. 用第2步下载的zip包中的 libraries 文件夹来代替第3步下载的文件夹中 的libraries

    \n
  8. \n
  9. 将第一步下载的4个zip包分别重命名:

    \n
    1
    2
    3
    4
    x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip  -->  xtensa-lx106-elf.zip
    x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip
    python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip
    x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip
    \n

    然后解压到在第三步的 tool文件夹中,下面是解压后:

    \n

    \"image-20210807203943403\"

    \n
  10. \n
\n
\n

我已经把这5步全部做完,放到压缩包 esp8266.zip 中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:

\n
1
C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
\n
\n"},{"title":"esp8266(2)-网络服务器","abbrlink":"27153554","date":"2021-08-02T00:20:00.000Z","description":"esp8266作为服务器使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n## 1、建立基本网络服务器\n\n\n\n1. 导入库文件 `ESP8266WebServer.h`\n\n2. 创建服务器对象\n\n ```c\n ESP8266WebServer server(80); //80是端口号\n ```\n\n3. 启动服务器\n\n ```c\n server.begin();\n ```\n\n4. 服务器访问配置\n\n ```c\n // 访问根节点时,调用处理函数 handleRoot\n server.on(\"/\", handleRoot);\n \n //访问不到,即404时,调用处理函数 handleNotFound\n server.onNotFound(handleNotFound);\n ```\n\n5. 访问处理函数\n\n ```c\n void handleRoot(){\n server.send(200, \"text/plain\", \"Hello from ESP8266\");\n }\n ```\n\n ```c\n void handleNotFound(){\n server.send(404, \"text/plain\", \"404: Not found\");\n }\n ```\n\n \n\n6. 循环监听端口\n\n ```c\n void loop(){\n server.handleClient(); //循环监听客户端访问情况\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n server.begin();\n server.on(\"/\", handleRoot);\n server.onNotFound(handleNotFound);\n\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\n//监听客户端\nvoid loop(){\n server.handleClient();\n}\n\n//访问处理函数\nvoid handleRoot(){\n server.send(200, \"text/plain\", \"Hello esp8266\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n\n\n## 2、通过网络服务器实现开发板控制\n\n通过网页控制nodeMCU开发板小灯亮灭\n\n步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动\n\n3. 服务器访问配置\n\n ```c\n server.on(\"/\", HTTP_GET, handleRoot);\n \n /*添加这个控制LED的网页, 使用post方式发送http请求*/\n server.on(\"/LED\", HTTP_POST, handleLED); \n \n server.onNotFound(handleNotFound);\n ```\n\n4. 访问处理函数\n\n ```c\n \n void handleRoot(){\n server.send(200, \"text/html\", \"
\");\n }\n ```\n\n > 注意这句html代码:\n >\n > ```html\n >
\n > \n >
\n > ```\n >\n > 这会生成一个按钮,点击这个按钮会把数据发到\"/LED\"页面\n\n \n\n 下面是“/LED”页面的处理函数:\n\n ```c\n void handleLED(){\n static bool LEDState = LOW;//记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n }\n ```\n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.on(\"/LED\", HTTP_POST, handleLED);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n}\n\nvoid handleRoot(){\n server.send(200, \"text/html\", \"
\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n\nvoid handleLED(){\n static bool LEDState = LOW; //记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n}\n```\n\n\n\n## 3、将开发板引脚状态发送到终端网页\n\n将引脚**D3**(已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中\n\n1. 首先在开始时将D3引脚设置为上拉输入模式\n\n ```c\n pinMode(D3, INPUT_PULLUP); \n ```\n\n2. 循环读取引脚状态 `digitalRead()`\n\n ```c\n bool pinState;\n void loop(){\n pinState = digitalRead(D3); // 获取引脚状态\n //......\n }\n ```\n\n \n\n3. 要在根节点下显示引脚状态,所以其访问处理函数为:\n\n ```c\n \n void handleRoot() { \n String displayPinState; // 存储按键状态的字符串变量\n \n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n esp8266_server.send(200, \"text/plain\", displayPinState); \n // 向浏览器发送按键状态信息 \n }\n ```\n\n \n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nbool pinState;\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(D3, INPUT_PULLUP); //上拉输入模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(800);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n pinState = digitalRead(D3);\n}\n\nvoid handleRoot(){\n String displayPinState;\n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n server.send(200, \"text/plain\", displayPinState);\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n> 注意:引脚状态改变后需要手动刷新网页,网页显示才会改变\n\n\n\n\n\n","source":"_posts/esp8266/2-网络服务器.md","raw":"---\ntitle: esp8266(2)-网络服务器\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: '27153554'\ndate: 2021-08-02 08:20:00\ndescription: esp8266作为服务器使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n## 1、建立基本网络服务器\n\n\n\n1. 导入库文件 `ESP8266WebServer.h`\n\n2. 创建服务器对象\n\n ```c\n ESP8266WebServer server(80); //80是端口号\n ```\n\n3. 启动服务器\n\n ```c\n server.begin();\n ```\n\n4. 服务器访问配置\n\n ```c\n // 访问根节点时,调用处理函数 handleRoot\n server.on(\"/\", handleRoot);\n \n //访问不到,即404时,调用处理函数 handleNotFound\n server.onNotFound(handleNotFound);\n ```\n\n5. 访问处理函数\n\n ```c\n void handleRoot(){\n server.send(200, \"text/plain\", \"Hello from ESP8266\");\n }\n ```\n\n ```c\n void handleNotFound(){\n server.send(404, \"text/plain\", \"404: Not found\");\n }\n ```\n\n \n\n6. 循环监听端口\n\n ```c\n void loop(){\n server.handleClient(); //循环监听客户端访问情况\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n server.begin();\n server.on(\"/\", handleRoot);\n server.onNotFound(handleNotFound);\n\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\n//监听客户端\nvoid loop(){\n server.handleClient();\n}\n\n//访问处理函数\nvoid handleRoot(){\n server.send(200, \"text/plain\", \"Hello esp8266\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n\n\n## 2、通过网络服务器实现开发板控制\n\n通过网页控制nodeMCU开发板小灯亮灭\n\n步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动\n\n3. 服务器访问配置\n\n ```c\n server.on(\"/\", HTTP_GET, handleRoot);\n \n /*添加这个控制LED的网页, 使用post方式发送http请求*/\n server.on(\"/LED\", HTTP_POST, handleLED); \n \n server.onNotFound(handleNotFound);\n ```\n\n4. 访问处理函数\n\n ```c\n \n void handleRoot(){\n server.send(200, \"text/html\", \"
\");\n }\n ```\n\n > 注意这句html代码:\n >\n > ```html\n >
\n > \n >
\n > ```\n >\n > 这会生成一个按钮,点击这个按钮会把数据发到\"/LED\"页面\n\n \n\n 下面是“/LED”页面的处理函数:\n\n ```c\n void handleLED(){\n static bool LEDState = LOW;//记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n }\n ```\n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.on(\"/LED\", HTTP_POST, handleLED);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n}\n\nvoid handleRoot(){\n server.send(200, \"text/html\", \"
\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n\nvoid handleLED(){\n static bool LEDState = LOW; //记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n}\n```\n\n\n\n## 3、将开发板引脚状态发送到终端网页\n\n将引脚**D3**(已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中\n\n1. 首先在开始时将D3引脚设置为上拉输入模式\n\n ```c\n pinMode(D3, INPUT_PULLUP); \n ```\n\n2. 循环读取引脚状态 `digitalRead()`\n\n ```c\n bool pinState;\n void loop(){\n pinState = digitalRead(D3); // 获取引脚状态\n //......\n }\n ```\n\n \n\n3. 要在根节点下显示引脚状态,所以其访问处理函数为:\n\n ```c\n \n void handleRoot() { \n String displayPinState; // 存储按键状态的字符串变量\n \n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n esp8266_server.send(200, \"text/plain\", displayPinState); \n // 向浏览器发送按键状态信息 \n }\n ```\n\n \n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nbool pinState;\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(D3, INPUT_PULLUP); //上拉输入模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(800);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n pinState = digitalRead(D3);\n}\n\nvoid handleRoot(){\n String displayPinState;\n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n server.send(200, \"text/plain\", displayPinState);\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n> 注意:引脚状态改变后需要手动刷新网页,网页显示才会改变\n\n\n\n\n\n","slug":"esp8266/2-网络服务器","published":1,"updated":"2021-09-10T03:19:57.136Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pt003xakve53qs5vxp","content":"

1、建立基本网络服务器

    \n
  1. 导入库文件 ESP8266WebServer.h

    \n
  2. \n
  3. 创建服务器对象

    \n
    1
    ESP8266WebServer server(80);  //80是端口号
    \n
  4. \n
  5. 启动服务器

    \n
    1
    server.begin();
    \n
  6. \n
  7. 服务器访问配置

    \n
    1
    2
    3
    4
    5
    // 访问根节点时,调用处理函数 handleRoot
    server.on("/", handleRoot);

    //访问不到,即404时,调用处理函数 handleNotFound
    server.onNotFound(handleNotFound);
    \n
  8. \n
  9. 访问处理函数

    \n
    1
    2
    3
    void handleRoot(){
    server.send(200, "text/plain", "Hello from ESP8266");
    }
    \n
    1
    2
    3
    void handleNotFound(){
    server.send(404, "text/plain", "404: Not found");
    }
    \n
  10. \n
\n
    \n
  1. 循环监听端口

    \n
    1
    2
    3
    void loop(){
    server.handleClient(); //循环监听客户端访问情况
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

void setup(){
Serial.begin(9600);
server.begin();
server.on("/", handleRoot);
server.onNotFound(handleNotFound);

wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(600);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());

}

//监听客户端
void loop(){
server.handleClient();
}

//访问处理函数
void handleRoot(){
server.send(200, "text/plain", "Hello esp8266");
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}
\n

2、通过网络服务器实现开发板控制

通过网页控制nodeMCU开发板小灯亮灭

\n

步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动

\n
    \n
  1. 服务器访问配置

    \n
    1
    2
    3
    4
    5
    6
    server.on("/", HTTP_GET, handleRoot);

    /*添加这个控制LED的网页, 使用post方式发送http请求*/
    server.on("/LED", HTTP_POST, handleLED);

    server.onNotFound(handleNotFound);
    \n
  2. \n
  3. 访问处理函数

    \n
    1
    2
    3
    4

    void handleRoot(){
    server.send(200, "text/html", "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>");
    }
    \n
    \n

    注意这句html代码:

    \n
    1
    2
    3
    <form action="/LED" method="POST">
    <input type="submit" value="Toggle LED">
    </form>
    \n

    这会生成一个按钮,点击这个按钮会把数据发到”/LED”页面

    \n
    \n
  4. \n
\n

下面是“/LED”页面的处理函数:

\n
1
2
3
4
5
6
7
void handleLED(){
static bool LEDState = LOW;//记录LED此时亮灭状态
LEDState = !LEDState;
digitalWrite(LED_BUILTIN, LEDState);
server.sendHeader("Location", "/");
server.send(303); //303表示将网页重定向
}
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

void setup(){
Serial.begin(9600);

pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式

/*服务器配置*/
server.begin();
server.on("/", HTTP_GET, handleRoot);
server.on("/LED", HTTP_POST, handleLED);
server.onNotFound(handleNotFound);

/*Wifi配置*/
wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(600);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}

void loop(){
server.handleClient();
}

void handleRoot(){
server.send(200, "text/html", "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>");
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}

void handleLED(){
static bool LEDState = LOW; //记录LED此时亮灭状态
LEDState = !LEDState;
digitalWrite(LED_BUILTIN, LEDState);
server.sendHeader("Location", "/");
server.send(303); //303表示将网页重定向
}
\n

3、将开发板引脚状态发送到终端网页

将引脚D3(已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中

\n
    \n
  1. 首先在开始时将D3引脚设置为上拉输入模式

    \n
    1
    pinMode(D3, INPUT_PULLUP); 
    \n
  2. \n
  3. 循环读取引脚状态 digitalRead()

    \n
    1
    2
    3
    4
    5
    bool pinState;
    void loop(){
    pinState = digitalRead(D3); // 获取引脚状态
    //......
    }
    \n
  4. \n
\n
    \n
  1. 要在根节点下显示引脚状态,所以其访问处理函数为:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    void handleRoot() {
    String displayPinState; // 存储按键状态的字符串变量

    if(pinState == HIGH){ // 当按键引脚D3为高电平
    displayPinState = "Button State: HIGH"; // 字符串赋值高电平信息
    } else { // 当按键引脚D3为低电平
    displayPinState = "Button State: LOW"; // 字符串赋值低电平信息
    }
    esp8266_server.send(200, "text/plain", displayPinState);
    // 向浏览器发送按键状态信息
    }
    \n
  2. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

bool pinState;

void setup(){
Serial.begin(9600);

pinMode(D3, INPUT_PULLUP); //上拉输入模式

/*服务器配置*/
server.begin();
server.on("/", HTTP_GET, handleRoot);
server.onNotFound(handleNotFound);

/*Wifi配置*/
wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(800);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}

void loop(){
server.handleClient();
pinState = digitalRead(D3);
}

void handleRoot(){
String displayPinState;
if(pinState == HIGH){ // 当按键引脚D3为高电平
displayPinState = "Button State: HIGH"; // 字符串赋值高电平信息
} else { // 当按键引脚D3为低电平
displayPinState = "Button State: LOW"; // 字符串赋值低电平信息
}
server.send(200, "text/plain", displayPinState);
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}
\n
\n

注意:引脚状态改变后需要手动刷新网页,网页显示才会改变

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

1、建立基本网络服务器

    \n
  1. 导入库文件 ESP8266WebServer.h

    \n
  2. \n
  3. 创建服务器对象

    \n
    1
    ESP8266WebServer server(80);  //80是端口号
    \n
  4. \n
  5. 启动服务器

    \n
    1
    server.begin();
    \n
  6. \n
  7. 服务器访问配置

    \n
    1
    2
    3
    4
    5
    // 访问根节点时,调用处理函数 handleRoot
    server.on("/", handleRoot);

    //访问不到,即404时,调用处理函数 handleNotFound
    server.onNotFound(handleNotFound);
    \n
  8. \n
  9. 访问处理函数

    \n
    1
    2
    3
    void handleRoot(){
    server.send(200, "text/plain", "Hello from ESP8266");
    }
    \n
    1
    2
    3
    void handleNotFound(){
    server.send(404, "text/plain", "404: Not found");
    }
    \n
  10. \n
\n
    \n
  1. 循环监听端口

    \n
    1
    2
    3
    void loop(){
    server.handleClient(); //循环监听客户端访问情况
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

void setup(){
Serial.begin(9600);
server.begin();
server.on("/", handleRoot);
server.onNotFound(handleNotFound);

wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(600);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());

}

//监听客户端
void loop(){
server.handleClient();
}

//访问处理函数
void handleRoot(){
server.send(200, "text/plain", "Hello esp8266");
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}
\n

2、通过网络服务器实现开发板控制

通过网页控制nodeMCU开发板小灯亮灭

\n

步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动

\n
    \n
  1. 服务器访问配置

    \n
    1
    2
    3
    4
    5
    6
    server.on("/", HTTP_GET, handleRoot);

    /*添加这个控制LED的网页, 使用post方式发送http请求*/
    server.on("/LED", HTTP_POST, handleLED);

    server.onNotFound(handleNotFound);
    \n
  2. \n
  3. 访问处理函数

    \n
    1
    2
    3
    4

    void handleRoot(){
    server.send(200, "text/html", "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>");
    }
    \n
    \n

    注意这句html代码:

    \n
    1
    2
    3
    <form action="/LED" method="POST">
    <input type="submit" value="Toggle LED">
    </form>
    \n

    这会生成一个按钮,点击这个按钮会把数据发到”/LED”页面

    \n
    \n
  4. \n
\n

下面是“/LED”页面的处理函数:

\n
1
2
3
4
5
6
7
void handleLED(){
static bool LEDState = LOW;//记录LED此时亮灭状态
LEDState = !LEDState;
digitalWrite(LED_BUILTIN, LEDState);
server.sendHeader("Location", "/");
server.send(303); //303表示将网页重定向
}
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

void setup(){
Serial.begin(9600);

pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式

/*服务器配置*/
server.begin();
server.on("/", HTTP_GET, handleRoot);
server.on("/LED", HTTP_POST, handleLED);
server.onNotFound(handleNotFound);

/*Wifi配置*/
wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(600);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}

void loop(){
server.handleClient();
}

void handleRoot(){
server.send(200, "text/html", "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>");
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}

void handleLED(){
static bool LEDState = LOW; //记录LED此时亮灭状态
LEDState = !LEDState;
digitalWrite(LED_BUILTIN, LEDState);
server.sendHeader("Location", "/");
server.send(303); //303表示将网页重定向
}
\n

3、将开发板引脚状态发送到终端网页

将引脚D3(已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中

\n
    \n
  1. 首先在开始时将D3引脚设置为上拉输入模式

    \n
    1
    pinMode(D3, INPUT_PULLUP); 
    \n
  2. \n
  3. 循环读取引脚状态 digitalRead()

    \n
    1
    2
    3
    4
    5
    bool pinState;
    void loop(){
    pinState = digitalRead(D3); // 获取引脚状态
    //......
    }
    \n
  4. \n
\n
    \n
  1. 要在根节点下显示引脚状态,所以其访问处理函数为:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    void handleRoot() {
    String displayPinState; // 存储按键状态的字符串变量

    if(pinState == HIGH){ // 当按键引脚D3为高电平
    displayPinState = "Button State: HIGH"; // 字符串赋值高电平信息
    } else { // 当按键引脚D3为低电平
    displayPinState = "Button State: LOW"; // 字符串赋值低电平信息
    }
    esp8266_server.send(200, "text/plain", displayPinState);
    // 向浏览器发送按键状态信息
    }
    \n
  2. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

bool pinState;

void setup(){
Serial.begin(9600);

pinMode(D3, INPUT_PULLUP); //上拉输入模式

/*服务器配置*/
server.begin();
server.on("/", HTTP_GET, handleRoot);
server.onNotFound(handleNotFound);

/*Wifi配置*/
wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(800);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}

void loop(){
server.handleClient();
pinState = digitalRead(D3);
}

void handleRoot(){
String displayPinState;
if(pinState == HIGH){ // 当按键引脚D3为高电平
displayPinState = "Button State: HIGH"; // 字符串赋值高电平信息
} else { // 当按键引脚D3为低电平
displayPinState = "Button State: LOW"; // 字符串赋值低电平信息
}
server.send(200, "text/plain", displayPinState);
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}
\n
\n

注意:引脚状态改变后需要手动刷新网页,网页显示才会改变

\n
\n"},{"title":"esp8266(4)-NodeMCU作为客户端使用","abbrlink":"d150c3f1","date":"2021-08-03T03:21:00.000Z","description":"esp8266作为客户端如何连接服务器","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\nESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是`WiFiClient`库和`ESP8266HTTPClient`库。\n\n【注意】\n\n- `WiFiClient` 被声明在 `` 中(这种方式实现Client更复杂,但灵活)\n\n- 而`ESP8266HTTPClient`被单独声明在 ``(这种方式实现Client更简单,但很多功能不能根据自己意愿来定制实现)\n\n\n\n\n\n## 1、ESP8266HTTPClient 实现\n\n1. 连接WiFi,具体实现看第一篇文章\n\n2. 连接WiFi后,写一个函数来访问服务器\n\n3. 函数设计,五个步骤:\n\n - 创建客户端对象\n - 配置访问地址url\n - 发送get(post)请求\n - 请求成功:处理返回信息\n - 请求失败:(任意)\n - 结束请求\n\n ```c\n // 发送HTTP请求并且将服务器响应通过串口输出\n void httpClientRequest(){\n \n //1 创建 HTTPClient 对象\n HTTPClient httpClient;\n \n //2 通过begin函数配置请求地址\n httpClient.begin(URL); \n \n //3 通过GET函数启动连接并发送HTTP请求\n int httpCode = httpClient.GET();\n \n //4 处理服务器返回信息\n if (httpCode == HTTP_CODE_OK) {\n // 使用getString函数获取服务器响应体内容\n String responsePayload = httpClient.getString();\n //...\n } else {\n //...\n }\n \n //5 关闭ESP8266与服务器连接\n httpClient.end();\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n\nvoid setup() {\n String url = \"http://www.example.com\";\n \n Serial.begin(115200);\n \n WiFi.mode(WIFI_STA);\n WiFi.begin(\"RBook\", \"17191719\");\n\n for(int i=0; WiFi.status() != WL_CONNECTED; i++){\n delay(1000);\n Serial.printf(\"%d \", i);\n }\n Serial.print(\"\\r\\nwifi is connected\\r\\n\");\n\n getResponse(url);\n \n}\n\nvoid loop() {\n // put your main code here, to run repeatedly:\n\n}\n\n//五个步骤进行发送请求并获取数据\nvoid getResponse(String url){\n \n HTTPClient httpClient; // 1\n\n httpClient.begin(url); // 2\n\n int httpCode = httpClient.GET(); // 3\n\n // 4\n if(httpCode == HTTP_CODE_OK){\n Serial.printf(\"httpCode: %d Success......\\n\", httpCode);\n String res = httpClient.getString();\n Serial.println(\"======================\");\n Serial.println(res);\n Serial.println(\"======================\");\n }\n else {\n Serial.printf(\"httpCode: %d Failed......\\n\", httpCode);\n }\n\n // 5\n httpClient.end();\n \n}\n\n```\n\n##### 【注意】\n\n> 转义字符 `\\r` 表示将光标移动到本行开头(继续输出会覆盖本行内容)\n>\n> `\\r\\n` 表示将光标移到开头再换行(不会覆盖本行内容),一般与 `\\n` 效果相同\n\n\n\n## 2、WiFiClient 实现\n\n1. 创建对象\n2. 连接服务器\n - 连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接\n - 连接失败:断开连接\n\n```c\nvoid wifiClientRequest(String url){\n // 建立WiFi客户端对象\n WiFiClient client; \n\n // 建立字符串,用于HTTP请求\n String httpRequest = String(\"GET /\") + \" HTTP/1.1\\r\\n\" +\n \"url: \" + url + \"\\r\\n\" +\n \"Connection: close\\r\\n\" +\n \"\\r\\n\";\n\n // 连接网络服务器\n if (client.connect(url, httpPort)){ \t//连接成功connect会返回true\n \n client.print(httpRequest); // 向服务器发送HTTP请求\n\n // 通过串口输出网络服务器响应信息 \n while (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //一行一行读取\n Serial.println(line);\n }\n }\n\n client.stop();\t\t// 断开与服务器的连接\n\n } else{ \n client.stop();\t\t// 如果连接不成功则断开连接\n }\n```\n\n\n\n## ","source":"_posts/esp8266/4-NodeMCU作为客户端使用.md","raw":"---\ntitle: esp8266(4)-NodeMCU作为客户端使用\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: d150c3f1\ndate: 2021-08-03 11:21:00\ndescription: esp8266作为客户端如何连接服务器\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\nESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是`WiFiClient`库和`ESP8266HTTPClient`库。\n\n【注意】\n\n- `WiFiClient` 被声明在 `` 中(这种方式实现Client更复杂,但灵活)\n\n- 而`ESP8266HTTPClient`被单独声明在 ``(这种方式实现Client更简单,但很多功能不能根据自己意愿来定制实现)\n\n\n\n\n\n## 1、ESP8266HTTPClient 实现\n\n1. 连接WiFi,具体实现看第一篇文章\n\n2. 连接WiFi后,写一个函数来访问服务器\n\n3. 函数设计,五个步骤:\n\n - 创建客户端对象\n - 配置访问地址url\n - 发送get(post)请求\n - 请求成功:处理返回信息\n - 请求失败:(任意)\n - 结束请求\n\n ```c\n // 发送HTTP请求并且将服务器响应通过串口输出\n void httpClientRequest(){\n \n //1 创建 HTTPClient 对象\n HTTPClient httpClient;\n \n //2 通过begin函数配置请求地址\n httpClient.begin(URL); \n \n //3 通过GET函数启动连接并发送HTTP请求\n int httpCode = httpClient.GET();\n \n //4 处理服务器返回信息\n if (httpCode == HTTP_CODE_OK) {\n // 使用getString函数获取服务器响应体内容\n String responsePayload = httpClient.getString();\n //...\n } else {\n //...\n }\n \n //5 关闭ESP8266与服务器连接\n httpClient.end();\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n\nvoid setup() {\n String url = \"http://www.example.com\";\n \n Serial.begin(115200);\n \n WiFi.mode(WIFI_STA);\n WiFi.begin(\"RBook\", \"17191719\");\n\n for(int i=0; WiFi.status() != WL_CONNECTED; i++){\n delay(1000);\n Serial.printf(\"%d \", i);\n }\n Serial.print(\"\\r\\nwifi is connected\\r\\n\");\n\n getResponse(url);\n \n}\n\nvoid loop() {\n // put your main code here, to run repeatedly:\n\n}\n\n//五个步骤进行发送请求并获取数据\nvoid getResponse(String url){\n \n HTTPClient httpClient; // 1\n\n httpClient.begin(url); // 2\n\n int httpCode = httpClient.GET(); // 3\n\n // 4\n if(httpCode == HTTP_CODE_OK){\n Serial.printf(\"httpCode: %d Success......\\n\", httpCode);\n String res = httpClient.getString();\n Serial.println(\"======================\");\n Serial.println(res);\n Serial.println(\"======================\");\n }\n else {\n Serial.printf(\"httpCode: %d Failed......\\n\", httpCode);\n }\n\n // 5\n httpClient.end();\n \n}\n\n```\n\n##### 【注意】\n\n> 转义字符 `\\r` 表示将光标移动到本行开头(继续输出会覆盖本行内容)\n>\n> `\\r\\n` 表示将光标移到开头再换行(不会覆盖本行内容),一般与 `\\n` 效果相同\n\n\n\n## 2、WiFiClient 实现\n\n1. 创建对象\n2. 连接服务器\n - 连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接\n - 连接失败:断开连接\n\n```c\nvoid wifiClientRequest(String url){\n // 建立WiFi客户端对象\n WiFiClient client; \n\n // 建立字符串,用于HTTP请求\n String httpRequest = String(\"GET /\") + \" HTTP/1.1\\r\\n\" +\n \"url: \" + url + \"\\r\\n\" +\n \"Connection: close\\r\\n\" +\n \"\\r\\n\";\n\n // 连接网络服务器\n if (client.connect(url, httpPort)){ \t//连接成功connect会返回true\n \n client.print(httpRequest); // 向服务器发送HTTP请求\n\n // 通过串口输出网络服务器响应信息 \n while (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //一行一行读取\n Serial.println(line);\n }\n }\n\n client.stop();\t\t// 断开与服务器的连接\n\n } else{ \n client.stop();\t\t// 如果连接不成功则断开连接\n }\n```\n\n\n\n## ","slug":"esp8266/4-NodeMCU作为客户端使用","published":1,"updated":"2021-09-11T07:54:22.484Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pu003zakve3wlwdsem","content":"

ESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是WiFiClient库和ESP8266HTTPClient库。

\n

【注意】

\n
    \n
  • WiFiClient 被声明在 <ESP8266WiFi.h> 中(这种方式实现Client更复杂,但灵活)

    \n
  • \n
  • ESP8266HTTPClient被单独声明在 <ESP8266HTTPClient.h>(这种方式实现Client更简单,但很多功能不能根据自己意愿来定制实现)

    \n
  • \n
\n

1、ESP8266HTTPClient 实现

    \n
  1. 连接WiFi,具体实现看第一篇文章

    \n
  2. \n
  3. 连接WiFi后,写一个函数来访问服务器

    \n
  4. \n
  5. 函数设计,五个步骤:

    \n
      \n
    • 创建客户端对象
    • \n
    • 配置访问地址url
    • \n
    • 发送get(post)请求
        \n
      • 请求成功:处理返回信息
      • \n
      • 请求失败:(任意)
      • \n
      \n
    • \n
    • 结束请求
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 发送HTTP请求并且将服务器响应通过串口输出
    void httpClientRequest(){

    //1 创建 HTTPClient 对象
    HTTPClient httpClient;

    //2 通过begin函数配置请求地址
    httpClient.begin(URL);

    //3 通过GET函数启动连接并发送HTTP请求
    int httpCode = httpClient.GET();

    //4 处理服务器返回信息
    if (httpCode == HTTP_CODE_OK) {
    // 使用getString函数获取服务器响应体内容
    String responsePayload = httpClient.getString();
    //...
    } else {
    //...
    }

    //5 关闭ESP8266与服务器连接
    httpClient.end();
    }
    \n
  6. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

void setup() {
String url = "http://www.example.com";

Serial.begin(115200);

WiFi.mode(WIFI_STA);
WiFi.begin("RBook", "17191719");

for(int i=0; WiFi.status() != WL_CONNECTED; i++){
delay(1000);
Serial.printf("%d ", i);
}
Serial.print("\\r\\nwifi is connected\\r\\n");

getResponse(url);

}

void loop() {
// put your main code here, to run repeatedly:

}

//五个步骤进行发送请求并获取数据
void getResponse(String url){

HTTPClient httpClient; // 1

httpClient.begin(url); // 2

int httpCode = httpClient.GET(); // 3

// 4
if(httpCode == HTTP_CODE_OK){
Serial.printf("httpCode: %d Success......\\n", httpCode);
String res = httpClient.getString();
Serial.println("======================");
Serial.println(res);
Serial.println("======================");
}
else {
Serial.printf("httpCode: %d Failed......\\n", httpCode);
}

// 5
httpClient.end();

}

\n
【注意】
\n

转义字符 \\r 表示将光标移动到本行开头(继续输出会覆盖本行内容)

\n

\\r\\n 表示将光标移到开头再换行(不会覆盖本行内容),一般与 \\n 效果相同

\n
\n

2、WiFiClient 实现

    \n
  1. 创建对象
  2. \n
  3. 连接服务器
      \n
    • 连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接
    • \n
    • 连接失败:断开连接
    • \n
    \n
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void wifiClientRequest(String url){
// 建立WiFi客户端对象
WiFiClient client;

// 建立字符串,用于HTTP请求
String httpRequest = String("GET /") + " HTTP/1.1\\r\\n" +
"url: " + url + "\\r\\n" +
"Connection: close\\r\\n" +
"\\r\\n";

// 连接网络服务器
if (client.connect(url, httpPort)){ \t//连接成功connect会返回true

client.print(httpRequest); // 向服务器发送HTTP请求

// 通过串口输出网络服务器响应信息
while (client.connected() || client.available()){
if (client.available()){
String line = client.readStringUntil('\\n'); //一行一行读取
Serial.println(line);
}
}

client.stop();\t\t// 断开与服务器的连接

} else{
client.stop();\t\t// 如果连接不成功则断开连接
}
\n

","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

ESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是WiFiClient库和ESP8266HTTPClient库。

\n

【注意】

\n
    \n
  • WiFiClient 被声明在 <ESP8266WiFi.h> 中(这种方式实现Client更复杂,但灵活)

    \n
  • \n
  • ESP8266HTTPClient被单独声明在 <ESP8266HTTPClient.h>(这种方式实现Client更简单,但很多功能不能根据自己意愿来定制实现)

    \n
  • \n
\n

1、ESP8266HTTPClient 实现

    \n
  1. 连接WiFi,具体实现看第一篇文章

    \n
  2. \n
  3. 连接WiFi后,写一个函数来访问服务器

    \n
  4. \n
  5. 函数设计,五个步骤:

    \n
      \n
    • 创建客户端对象
    • \n
    • 配置访问地址url
    • \n
    • 发送get(post)请求
        \n
      • 请求成功:处理返回信息
      • \n
      • 请求失败:(任意)
      • \n
      \n
    • \n
    • 结束请求
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 发送HTTP请求并且将服务器响应通过串口输出
    void httpClientRequest(){

    //1 创建 HTTPClient 对象
    HTTPClient httpClient;

    //2 通过begin函数配置请求地址
    httpClient.begin(URL);

    //3 通过GET函数启动连接并发送HTTP请求
    int httpCode = httpClient.GET();

    //4 处理服务器返回信息
    if (httpCode == HTTP_CODE_OK) {
    // 使用getString函数获取服务器响应体内容
    String responsePayload = httpClient.getString();
    //...
    } else {
    //...
    }

    //5 关闭ESP8266与服务器连接
    httpClient.end();
    }
    \n
  6. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

void setup() {
String url = "http://www.example.com";

Serial.begin(115200);

WiFi.mode(WIFI_STA);
WiFi.begin("RBook", "17191719");

for(int i=0; WiFi.status() != WL_CONNECTED; i++){
delay(1000);
Serial.printf("%d ", i);
}
Serial.print("\\r\\nwifi is connected\\r\\n");

getResponse(url);

}

void loop() {
// put your main code here, to run repeatedly:

}

//五个步骤进行发送请求并获取数据
void getResponse(String url){

HTTPClient httpClient; // 1

httpClient.begin(url); // 2

int httpCode = httpClient.GET(); // 3

// 4
if(httpCode == HTTP_CODE_OK){
Serial.printf("httpCode: %d Success......\\n", httpCode);
String res = httpClient.getString();
Serial.println("======================");
Serial.println(res);
Serial.println("======================");
}
else {
Serial.printf("httpCode: %d Failed......\\n", httpCode);
}

// 5
httpClient.end();

}

\n
【注意】
\n

转义字符 \\r 表示将光标移动到本行开头(继续输出会覆盖本行内容)

\n

\\r\\n 表示将光标移到开头再换行(不会覆盖本行内容),一般与 \\n 效果相同

\n
\n

2、WiFiClient 实现

    \n
  1. 创建对象
  2. \n
  3. 连接服务器
      \n
    • 连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接
    • \n
    • 连接失败:断开连接
    • \n
    \n
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void wifiClientRequest(String url){
// 建立WiFi客户端对象
WiFiClient client;

// 建立字符串,用于HTTP请求
String httpRequest = String("GET /") + " HTTP/1.1\\r\\n" +
"url: " + url + "\\r\\n" +
"Connection: close\\r\\n" +
"\\r\\n";

// 连接网络服务器
if (client.connect(url, httpPort)){ \t//连接成功connect会返回true

client.print(httpRequest); // 向服务器发送HTTP请求

// 通过串口输出网络服务器响应信息
while (client.connected() || client.available()){
if (client.available()){
String line = client.readStringUntil('\\n'); //一行一行读取
Serial.println(line);
}
}

client.stop();\t\t// 断开与服务器的连接

} else{
client.stop();\t\t// 如果连接不成功则断开连接
}
\n

"},{"title":"esp8266(5)-Stream数据流","abbrlink":"2d7f83ef","date":"2021-08-03T05:10:00.000Z","description":"esp8266读取串口传入的数据、文件系统的数据、服务器传来的数据","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n# Stream数据流\n\n## 1、串口收发数据\n\n```c\nvoid setup() {\n // 启动串口通讯\n Serial.begin(9600); \n Serial.println();\n}\n \nvoid loop() { \n if (Serial.available()){ // 当串口接收到信息后\n String serialData = Serial.readString(); // 将接收到的信息存储于serialData变量\n Serial.print(serialData); \n }\n}\n```\n\n使用`Serial.available`来判断ESP8266开发板是否接收到串口数据\n\n实际上,ESP8266开发板通过串口收发的数据通过Stream进行的\n\n> 下面的程序将演示:\n>\n> 当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。\n>\n> ```c\n> void setup() {\n> Serial.begin(9600);\n> Serial.println(\"\");\n> Serial.println(\"Please enter input...\");\n> }\n> \n> void loop() {\n> while(Serial.available()){\n> if(Serial.find(\"ok\")){\n> Serial.println(\"Found ok in user input.\");\n> \n> int serialParseInt = Serial.parseInt();\n> Serial.print(\"serialParseInt = \");\n> Serial.println(serialParseInt);\n> \n> String serialInput = Serial.readString();\n> Serial.print(\"serialInput = \");\n> Serial.println(serialInput);\n> }\n> }\n> }\n> ```\n>\n> \n\n\n\n\n\n## 2、使用Stream方式来读取服务器响应的信息\n\n(具体看第4篇文章第2点的代码)\n\n```c\nwhile (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //读取返回的数据\n Serial.println(line);\n }\n}\n```\n\n\n\n## 3、使用Stream方式来读取文件内容\n\n\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); // 以“r”模式再次打开闪存文件\nSerial.println(f.readString());\t\t\t\t// 读取文件内容\n```\n\n","source":"_posts/esp8266/5-Stream数据流.md","raw":"---\ntitle: esp8266(5)-Stream数据流\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 2d7f83ef\ndate: 2021-08-03 13:10:00\ndescription: esp8266读取串口传入的数据、文件系统的数据、服务器传来的数据\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n# Stream数据流\n\n## 1、串口收发数据\n\n```c\nvoid setup() {\n // 启动串口通讯\n Serial.begin(9600); \n Serial.println();\n}\n \nvoid loop() { \n if (Serial.available()){ // 当串口接收到信息后\n String serialData = Serial.readString(); // 将接收到的信息存储于serialData变量\n Serial.print(serialData); \n }\n}\n```\n\n使用`Serial.available`来判断ESP8266开发板是否接收到串口数据\n\n实际上,ESP8266开发板通过串口收发的数据通过Stream进行的\n\n> 下面的程序将演示:\n>\n> 当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。\n>\n> ```c\n> void setup() {\n> Serial.begin(9600);\n> Serial.println(\"\");\n> Serial.println(\"Please enter input...\");\n> }\n> \n> void loop() {\n> while(Serial.available()){\n> if(Serial.find(\"ok\")){\n> Serial.println(\"Found ok in user input.\");\n> \n> int serialParseInt = Serial.parseInt();\n> Serial.print(\"serialParseInt = \");\n> Serial.println(serialParseInt);\n> \n> String serialInput = Serial.readString();\n> Serial.print(\"serialInput = \");\n> Serial.println(serialInput);\n> }\n> }\n> }\n> ```\n>\n> \n\n\n\n\n\n## 2、使用Stream方式来读取服务器响应的信息\n\n(具体看第4篇文章第2点的代码)\n\n```c\nwhile (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //读取返回的数据\n Serial.println(line);\n }\n}\n```\n\n\n\n## 3、使用Stream方式来读取文件内容\n\n\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); // 以“r”模式再次打开闪存文件\nSerial.println(f.readString());\t\t\t\t// 读取文件内容\n```\n\n","slug":"esp8266/5-Stream数据流","published":1,"updated":"2021-09-11T07:54:38.317Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pv0042akve76zr0o90","content":"

Stream数据流

1、串口收发数据

1
2
3
4
5
6
7
8
9
10
11
12
void setup() {
// 启动串口通讯
Serial.begin(9600);
Serial.println();
}

void loop() {
if (Serial.available()){ // 当串口接收到信息后
String serialData = Serial.readString(); // 将接收到的信息存储于serialData变量
Serial.print(serialData);
}
}
\n

使用Serial.available来判断ESP8266开发板是否接收到串口数据

\n

实际上,ESP8266开发板通过串口收发的数据通过Stream进行的

\n
\n

下面的程序将演示:

\n

当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void setup() {
Serial.begin(9600);
Serial.println("");
Serial.println("Please enter input...");
}

void loop() {
while(Serial.available()){
if(Serial.find("ok")){
Serial.println("Found ok in user input.");

int serialParseInt = Serial.parseInt();
Serial.print("serialParseInt = ");
Serial.println(serialParseInt);

String serialInput = Serial.readString();
Serial.print("serialInput = ");
Serial.println(serialInput);
}
}
}
\n
\n

2、使用Stream方式来读取服务器响应的信息

(具体看第4篇文章第2点的代码)

\n
1
2
3
4
5
6
while (client.connected() || client.available()){ 
if (client.available()){
String line = client.readStringUntil('\\n'); //读取返回的数据
Serial.println(line);
}
}
\n

3、使用Stream方式来读取文件内容

1
2
File f = SPIFFS.open(file_name, "r");     // 以“r”模式再次打开闪存文件
Serial.println(f.readString());\t\t\t\t// 读取文件内容
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Stream数据流

1、串口收发数据

1
2
3
4
5
6
7
8
9
10
11
12
void setup() {
// 启动串口通讯
Serial.begin(9600);
Serial.println();
}

void loop() {
if (Serial.available()){ // 当串口接收到信息后
String serialData = Serial.readString(); // 将接收到的信息存储于serialData变量
Serial.print(serialData);
}
}
\n

使用Serial.available来判断ESP8266开发板是否接收到串口数据

\n

实际上,ESP8266开发板通过串口收发的数据通过Stream进行的

\n
\n

下面的程序将演示:

\n

当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void setup() {
Serial.begin(9600);
Serial.println("");
Serial.println("Please enter input...");
}

void loop() {
while(Serial.available()){
if(Serial.find("ok")){
Serial.println("Found ok in user input.");

int serialParseInt = Serial.parseInt();
Serial.print("serialParseInt = ");
Serial.println(serialParseInt);

String serialInput = Serial.readString();
Serial.print("serialInput = ");
Serial.println(serialInput);
}
}
}
\n
\n

2、使用Stream方式来读取服务器响应的信息

(具体看第4篇文章第2点的代码)

\n
1
2
3
4
5
6
while (client.connected() || client.available()){ 
if (client.available()){
String line = client.readStringUntil('\\n'); //读取返回的数据
Serial.println(line);
}
}
\n

3、使用Stream方式来读取文件内容

1
2
File f = SPIFFS.open(file_name, "r");     // 以“r”模式再次打开闪存文件
Serial.println(f.readString());\t\t\t\t// 读取文件内容
\n"},{"title":"esp8266(3)-闪存文件系统","abbrlink":"2080031a","date":"2021-08-03T01:00:00.000Z","description":"esp8266的文件系统使用(向闪存中写入信息、读取信息)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n## 1、文件基本操作\n\nESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS\n\n\n\n代码中需要用到头文件`` 包含的类`SPIFFS`\n\n下面教你如何打开一个文件并向里面写如信息\n\n1. 格式化SPIFFS\n\n ```c++\n SPIFFS.format(); \n ```\n\n2. 启动SPIFFS\n\n ```c++\n SPIFFS.begin(); //该函数会返回一个bool型结果,启动成功返回true,否则为false\n ```\n\n3. 用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)\n\n ```c++\n //以写入的方式打开一个文件,filename为文件路径\n String file_name = \"/folder/notes.txt\"\n File dataFile = SPIFFS.open(file_name, \"w\");\t\t\n dataFile.println(\"Hello World\"); // 向dataFile写入字符串信息\n dataFile.close(); // 关闭文件\n ```\n\n \n\n完整code:\n\n```c++\n#include \n\nvoid setup(){\n Serial.begin(9600);\n \n String filename = \"/folder/notes.txt\";\n SPIFFS.format(); \n if(SPIFFS.begin()) Serial.println(\"SPIFFS Started...\");\n else Serial.println(\"SPIFFS Failed to Start...\");\n\n File f = SPIFFS.open(filename, \"w\");\n f.println(\"Hello esp8266 world\");\n f.close();\n Serial.println(\"Finish writing the file...\");\n \n}\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n### SPIFFS基于文件的基本操作\n\n```c\nvoid SPIFFS.format(); //格式化闪存文件系统 【注意:格式化文件系统需要耗费一定时间】\n\nbool SPIFFS.exists(String s); //是否存在文件名为s的文件(准确说s是文件的路径)\nbool SPIFFS.remove(String s); //删除文件s,返回是否删除成功\n```\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); //以读的方式打开一个文件\n\n//打印文件中所有内容\nfor(int i=0; i 注意:不论以何种方式打开文件,最后都要记得关闭文件\n\n\n\n### 基于目录的操作\n\n\n\n获取一个目录对象 `openDir()`:\n\n```c\nString folder_name = \"/folder\"; //被读取的文件夹\nDir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象\n\n// dir.next()可以看作一个指针,每循环一次就会指向下一个元素\nwhile (dir.next()) { \n Serial.println(dir.fileName()); // 输出文件名\n}\n```\n\n\n\n\n\n\n\n### 闪存文件系统的基本信息\n\n```c\nFSInfo fs_info; // 创建一个基本信息的对象\n\n// 写入闪存文件系统信息到fs_info\nSPIFFS.info(fs_info);\n\nSerial.print(fs_info.totalBytes); // 可用空间总和(Bytes)\nSerial.print(fs_info.usedBytes); // 已经用掉的空间(Bytes)\n\n// 最大文件名字符限制(含路径和'\\0')\nSerial.println(fs_info.maxPathLength);\n\n// 最多允许打开文件数量\nSerial.println(fs_info.maxOpenFiles);\n\n// 存储块大小\nSerial.println(fs_info.blockSize);\n\n// 存储页大小\nSerial.println(fs_info.pageSize);\n```\n\n\n\n\n\n## 2、通过Arduino IDE向闪存文件系统上传文件\n\n\n\n看太极创客这篇[文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/upload-files/)\n\n##### 【注意】\n\n1. 上传文件前,把【串口监视器】关闭\n2. 之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了\n\n\n\n## 3、使用闪存系统配置功能更丰富的网络服务器\n\n[太极创客文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/spiffs-web-server/)\n\n","source":"_posts/esp8266/3-闪存文件系统.md","raw":"---\ntitle: esp8266(3)-闪存文件系统\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 2080031a\ndate: 2021-08-03 09:00:00\ndescription: esp8266的文件系统使用(向闪存中写入信息、读取信息)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n## 1、文件基本操作\n\nESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS\n\n\n\n代码中需要用到头文件`` 包含的类`SPIFFS`\n\n下面教你如何打开一个文件并向里面写如信息\n\n1. 格式化SPIFFS\n\n ```c++\n SPIFFS.format(); \n ```\n\n2. 启动SPIFFS\n\n ```c++\n SPIFFS.begin(); //该函数会返回一个bool型结果,启动成功返回true,否则为false\n ```\n\n3. 用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)\n\n ```c++\n //以写入的方式打开一个文件,filename为文件路径\n String file_name = \"/folder/notes.txt\"\n File dataFile = SPIFFS.open(file_name, \"w\");\t\t\n dataFile.println(\"Hello World\"); // 向dataFile写入字符串信息\n dataFile.close(); // 关闭文件\n ```\n\n \n\n完整code:\n\n```c++\n#include \n\nvoid setup(){\n Serial.begin(9600);\n \n String filename = \"/folder/notes.txt\";\n SPIFFS.format(); \n if(SPIFFS.begin()) Serial.println(\"SPIFFS Started...\");\n else Serial.println(\"SPIFFS Failed to Start...\");\n\n File f = SPIFFS.open(filename, \"w\");\n f.println(\"Hello esp8266 world\");\n f.close();\n Serial.println(\"Finish writing the file...\");\n \n}\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n### SPIFFS基于文件的基本操作\n\n```c\nvoid SPIFFS.format(); //格式化闪存文件系统 【注意:格式化文件系统需要耗费一定时间】\n\nbool SPIFFS.exists(String s); //是否存在文件名为s的文件(准确说s是文件的路径)\nbool SPIFFS.remove(String s); //删除文件s,返回是否删除成功\n```\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); //以读的方式打开一个文件\n\n//打印文件中所有内容\nfor(int i=0; i 注意:不论以何种方式打开文件,最后都要记得关闭文件\n\n\n\n### 基于目录的操作\n\n\n\n获取一个目录对象 `openDir()`:\n\n```c\nString folder_name = \"/folder\"; //被读取的文件夹\nDir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象\n\n// dir.next()可以看作一个指针,每循环一次就会指向下一个元素\nwhile (dir.next()) { \n Serial.println(dir.fileName()); // 输出文件名\n}\n```\n\n\n\n\n\n\n\n### 闪存文件系统的基本信息\n\n```c\nFSInfo fs_info; // 创建一个基本信息的对象\n\n// 写入闪存文件系统信息到fs_info\nSPIFFS.info(fs_info);\n\nSerial.print(fs_info.totalBytes); // 可用空间总和(Bytes)\nSerial.print(fs_info.usedBytes); // 已经用掉的空间(Bytes)\n\n// 最大文件名字符限制(含路径和'\\0')\nSerial.println(fs_info.maxPathLength);\n\n// 最多允许打开文件数量\nSerial.println(fs_info.maxOpenFiles);\n\n// 存储块大小\nSerial.println(fs_info.blockSize);\n\n// 存储页大小\nSerial.println(fs_info.pageSize);\n```\n\n\n\n\n\n## 2、通过Arduino IDE向闪存文件系统上传文件\n\n\n\n看太极创客这篇[文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/upload-files/)\n\n##### 【注意】\n\n1. 上传文件前,把【串口监视器】关闭\n2. 之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了\n\n\n\n## 3、使用闪存系统配置功能更丰富的网络服务器\n\n[太极创客文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/spiffs-web-server/)\n\n","slug":"esp8266/3-闪存文件系统","published":1,"updated":"2021-09-11T07:55:24.239Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pw0044akveh5zqdjte","content":"

1、文件基本操作

ESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS

\n

代码中需要用到头文件<FS.h> 包含的类SPIFFS

\n

下面教你如何打开一个文件并向里面写如信息

\n
    \n
  1. 格式化SPIFFS

    \n
    1
    SPIFFS.format(); 
    \n
  2. \n
  3. 启动SPIFFS

    \n
    1
    SPIFFS.begin();   //该函数会返回一个bool型结果,启动成功返回true,否则为false
    \n
  4. \n
  5. 用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)

    \n
    1
    2
    3
    4
    5
    //以写入的方式打开一个文件,filename为文件路径
    String file_name = "/folder/notes.txt"
    File dataFile = SPIFFS.open(file_name, "w");\t\t
    dataFile.println("Hello World"); // 向dataFile写入字符串信息
    dataFile.close(); // 关闭文件
    \n
  6. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <FS.h>

void setup(){
Serial.begin(9600);

String filename = "/folder/notes.txt";
SPIFFS.format();
if(SPIFFS.begin()) Serial.println("SPIFFS Started...");
else Serial.println("SPIFFS Failed to Start...");

File f = SPIFFS.open(filename, "w");
f.println("Hello esp8266 world");
f.close();
Serial.println("Finish writing the file...");

}

void loop(){

}
\n

SPIFFS基于文件的基本操作

1
2
3
4
void SPIFFS.format();   //格式化闪存文件系统 【注意:格式化文件系统需要耗费一定时间】

bool SPIFFS.exists(String s); //是否存在文件名为s的文件(准确说s是文件的路径)
bool SPIFFS.remove(String s); //删除文件s,返回是否删除成功
\n
1
2
3
4
5
6
File f = SPIFFS.open(file_name, "r");  //以读的方式打开一个文件

//打印文件中所有内容
for(int i=0; i<f.size(); i++){
Serial.print((char)f.read());
}
\n
1
2
3
4
//以添加信息方式打开一个文件
File f = SPIFFS.open(file_name, "a");
f.println("This is Appended Info."); // 添加字符串信息
f.close(); //关闭文件
\n
\n

注意:不论以何种方式打开文件,最后都要记得关闭文件

\n
\n

基于目录的操作

获取一个目录对象 openDir()

\n
1
2
3
4
5
6
7
String folder_name = "/folder";    //被读取的文件夹
Dir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象

// dir.next()可以看作一个指针,每循环一次就会指向下一个元素
while (dir.next()) {
Serial.println(dir.fileName()); // 输出文件名
}
\n

闪存文件系统的基本信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FSInfo fs_info;    // 创建一个基本信息的对象

// 写入闪存文件系统信息到fs_info
SPIFFS.info(fs_info);

Serial.print(fs_info.totalBytes); // 可用空间总和(Bytes)
Serial.print(fs_info.usedBytes); // 已经用掉的空间(Bytes)

// 最大文件名字符限制(含路径和'\\0')
Serial.println(fs_info.maxPathLength);

// 最多允许打开文件数量
Serial.println(fs_info.maxOpenFiles);

// 存储块大小
Serial.println(fs_info.blockSize);

// 存储页大小
Serial.println(fs_info.pageSize);
\n

2、通过Arduino IDE向闪存文件系统上传文件

看太极创客这篇文章

\n
【注意】
    \n
  1. 上传文件前,把【串口监视器】关闭
  2. \n
  3. 之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了
  4. \n
\n

3、使用闪存系统配置功能更丰富的网络服务器

太极创客文章

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

1、文件基本操作

ESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS

\n

代码中需要用到头文件<FS.h> 包含的类SPIFFS

\n

下面教你如何打开一个文件并向里面写如信息

\n
    \n
  1. 格式化SPIFFS

    \n
    1
    SPIFFS.format(); 
    \n
  2. \n
  3. 启动SPIFFS

    \n
    1
    SPIFFS.begin();   //该函数会返回一个bool型结果,启动成功返回true,否则为false
    \n
  4. \n
  5. 用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)

    \n
    1
    2
    3
    4
    5
    //以写入的方式打开一个文件,filename为文件路径
    String file_name = "/folder/notes.txt"
    File dataFile = SPIFFS.open(file_name, "w");\t\t
    dataFile.println("Hello World"); // 向dataFile写入字符串信息
    dataFile.close(); // 关闭文件
    \n
  6. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <FS.h>

void setup(){
Serial.begin(9600);

String filename = "/folder/notes.txt";
SPIFFS.format();
if(SPIFFS.begin()) Serial.println("SPIFFS Started...");
else Serial.println("SPIFFS Failed to Start...");

File f = SPIFFS.open(filename, "w");
f.println("Hello esp8266 world");
f.close();
Serial.println("Finish writing the file...");

}

void loop(){

}
\n

SPIFFS基于文件的基本操作

1
2
3
4
void SPIFFS.format();   //格式化闪存文件系统 【注意:格式化文件系统需要耗费一定时间】

bool SPIFFS.exists(String s); //是否存在文件名为s的文件(准确说s是文件的路径)
bool SPIFFS.remove(String s); //删除文件s,返回是否删除成功
\n
1
2
3
4
5
6
File f = SPIFFS.open(file_name, "r");  //以读的方式打开一个文件

//打印文件中所有内容
for(int i=0; i<f.size(); i++){
Serial.print((char)f.read());
}
\n
1
2
3
4
//以添加信息方式打开一个文件
File f = SPIFFS.open(file_name, "a");
f.println("This is Appended Info."); // 添加字符串信息
f.close(); //关闭文件
\n
\n

注意:不论以何种方式打开文件,最后都要记得关闭文件

\n
\n

基于目录的操作

获取一个目录对象 openDir()

\n
1
2
3
4
5
6
7
String folder_name = "/folder";    //被读取的文件夹
Dir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象

// dir.next()可以看作一个指针,每循环一次就会指向下一个元素
while (dir.next()) {
Serial.println(dir.fileName()); // 输出文件名
}
\n

闪存文件系统的基本信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FSInfo fs_info;    // 创建一个基本信息的对象

// 写入闪存文件系统信息到fs_info
SPIFFS.info(fs_info);

Serial.print(fs_info.totalBytes); // 可用空间总和(Bytes)
Serial.print(fs_info.usedBytes); // 已经用掉的空间(Bytes)

// 最大文件名字符限制(含路径和'\\0')
Serial.println(fs_info.maxPathLength);

// 最多允许打开文件数量
Serial.println(fs_info.maxOpenFiles);

// 存储块大小
Serial.println(fs_info.blockSize);

// 存储页大小
Serial.println(fs_info.pageSize);
\n

2、通过Arduino IDE向闪存文件系统上传文件

看太极创客这篇文章

\n
【注意】
    \n
  1. 上传文件前,把【串口监视器】关闭
  2. \n
  3. 之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了
  4. \n
\n

3、使用闪存系统配置功能更丰富的网络服务器

太极创客文章

\n"},{"title":"MQTT篇(一)MQTT的认识及原理和操作","abbrlink":"73ba8569","date":"2021-08-09T02:21:00.000Z","description":"MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n\n\n## 什么是MQTT\n\n\n\nMQTT是一个客户端服务端架构的发布/订阅模式的**消息传输协议**。\n\n优点:\n\n轻巧、开放、简单、规范,易于实现。\n\n这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。\n\n\n\n\n\n## 基本原理\n\n\n\n在MQTT协议通讯中,有两个最为重要的角色。它们分别是**服务端**和**客户端**。\n\n**服务端**:\n\n是消息传输的枢纽\n\n**客户端**:\n\n客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。\n\n客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。\n\n![image-20210810220807339](https://gitee.com/ajream/images/raw/master/img/2021-08-1022-08-14_image-20210810220807339.png)\n\n\n\n\n\n\n\n## 客户端连接服务端\n\n分2个步骤\n\n1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息\n2. 服务端收到请求后,发送数据包“CONNACK”进行确认\n\n\n\n### CONNECT报文\n\n这是该报文的信息:\n\n![MQTT CONNECT 报文内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-connect.gif)\n\n\n\n**clientID**\n\nclientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端\n\n\n\n**cleanSession – 清除会话**\n\n表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;\n\n取值true表示保存,false表示不保存\n\n> 注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。\n>\n> 反之如果数据不会重要,可以取值为false\n\n\n\n**keepAlive** —— 间隔时间\n\n用于服务端每隔多久就了解一下客户端是否与其保持连接的情况\n\n\n\n### CONNACK – 确认连接请求\n\n\n\n![MQTT CONNACK 信息内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/connack.gif)\n\n\n\n**sessionPresent**\n\nCONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。\n\ncleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。\n\n总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。\n\n\n\n**returnCode** —— 返回码\n\n当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况\n\n| 返回码 | 返回码描述 |\n| :----: | :----------------------------------------------------------- |\n| 0 | 成功连接 |\n| 1 | 连接被服务端拒绝,原因是不支持客户端的MQTT协议版本 |\n| 2 | 连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。 |\n| 3 | 连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。 |\n| 4 | 连接被服务端拒绝,原因是用户名或密码无效。 |\n| 5 | 连接被服务端拒绝,原因是客户端未被授权连接到此服务端。 |\n\n\n\n\n\n## ESP8266连接MQTT服务端\n\nhttp://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883\n\n接下来使用`PubSubClient`库(去[GitHub](https://github.com/knolleary/pubsubclient/)下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。\n\n1. 配置WiFi\n\n2. 创建mqtt客户端对象(间接创建)\n\n ```c\n WiFiClient wifiClient;\n PubSubClient mqttClient(wifiClient);\n ```\n\n3. 配置客户端要连接哪个服务端(绑定服务端)\n\n ```c\n String mqttServer = \"test.ranye-iot.net\";\n const int port = 1883;\n mqttClient.setServer(mqttServer, port); //连接服务端\n ```\n\n4. 连接服务端\n\n ```c\n //用设备的mac地址来生成唯一标识该设备的clientID\n String clientID = \"esp8266-\" + WiFi.macAddress(); \n \n mqttClient.connect(clientId.c_str()) //连接成功返回true\n ```\n\n \n\n5. 连接成功后保持 “心跳”,否则...(可以尝试继续连接)\n\n ```c\n void loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"rbook\";\nconst char* password = \"12345678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n \n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n \nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n \nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n}\n \nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n \nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","source":"_posts/esp8266/MQTT篇(一)-认识MQTT与基本操作.md","raw":"---\ntitle: MQTT篇(一)MQTT的认识及原理和操作\ntags:\n - 物联网\n - esp8266\ncategories:\n - 硬件学习\n - 物联网开发\nabbrlink: 73ba8569\ndate: 2021-08-09 10:21:00\ndescription: MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n\n\n## 什么是MQTT\n\n\n\nMQTT是一个客户端服务端架构的发布/订阅模式的**消息传输协议**。\n\n优点:\n\n轻巧、开放、简单、规范,易于实现。\n\n这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。\n\n\n\n\n\n## 基本原理\n\n\n\n在MQTT协议通讯中,有两个最为重要的角色。它们分别是**服务端**和**客户端**。\n\n**服务端**:\n\n是消息传输的枢纽\n\n**客户端**:\n\n客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。\n\n客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。\n\n![image-20210810220807339](https://gitee.com/ajream/images/raw/master/img/2021-08-1022-08-14_image-20210810220807339.png)\n\n\n\n\n\n\n\n## 客户端连接服务端\n\n分2个步骤\n\n1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息\n2. 服务端收到请求后,发送数据包“CONNACK”进行确认\n\n\n\n### CONNECT报文\n\n这是该报文的信息:\n\n![MQTT CONNECT 报文内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-connect.gif)\n\n\n\n**clientID**\n\nclientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端\n\n\n\n**cleanSession – 清除会话**\n\n表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;\n\n取值true表示保存,false表示不保存\n\n> 注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。\n>\n> 反之如果数据不会重要,可以取值为false\n\n\n\n**keepAlive** —— 间隔时间\n\n用于服务端每隔多久就了解一下客户端是否与其保持连接的情况\n\n\n\n### CONNACK – 确认连接请求\n\n\n\n![MQTT CONNACK 信息内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/connack.gif)\n\n\n\n**sessionPresent**\n\nCONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。\n\ncleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。\n\n总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。\n\n\n\n**returnCode** —— 返回码\n\n当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况\n\n| 返回码 | 返回码描述 |\n| :----: | :----------------------------------------------------------- |\n| 0 | 成功连接 |\n| 1 | 连接被服务端拒绝,原因是不支持客户端的MQTT协议版本 |\n| 2 | 连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。 |\n| 3 | 连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。 |\n| 4 | 连接被服务端拒绝,原因是用户名或密码无效。 |\n| 5 | 连接被服务端拒绝,原因是客户端未被授权连接到此服务端。 |\n\n\n\n\n\n## ESP8266连接MQTT服务端\n\nhttp://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883\n\n接下来使用`PubSubClient`库(去[GitHub](https://github.com/knolleary/pubsubclient/)下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。\n\n1. 配置WiFi\n\n2. 创建mqtt客户端对象(间接创建)\n\n ```c\n WiFiClient wifiClient;\n PubSubClient mqttClient(wifiClient);\n ```\n\n3. 配置客户端要连接哪个服务端(绑定服务端)\n\n ```c\n String mqttServer = \"test.ranye-iot.net\";\n const int port = 1883;\n mqttClient.setServer(mqttServer, port); //连接服务端\n ```\n\n4. 连接服务端\n\n ```c\n //用设备的mac地址来生成唯一标识该设备的clientID\n String clientID = \"esp8266-\" + WiFi.macAddress(); \n \n mqttClient.connect(clientId.c_str()) //连接成功返回true\n ```\n\n \n\n5. 连接成功后保持 “心跳”,否则...(可以尝试继续连接)\n\n ```c\n void loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"rbook\";\nconst char* password = \"12345678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n \n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n \nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n \nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n}\n \nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n \nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","slug":"esp8266/MQTT篇(一)-认识MQTT与基本操作","published":1,"updated":"2021-09-11T07:53:28.088Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6px0047akve8seydg2u","content":"

什么是MQTT

MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议

\n

优点:

\n

轻巧、开放、简单、规范,易于实现。

\n

这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。

\n

基本原理

在MQTT协议通讯中,有两个最为重要的角色。它们分别是服务端客户端

\n

服务端

\n

是消息传输的枢纽

\n

客户端

\n

客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。

\n

客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。

\n

\"image-20210810220807339\"

\n

客户端连接服务端

分2个步骤

\n
    \n
  1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息
  2. \n
  3. 服务端收到请求后,发送数据包“CONNACK”进行确认
  4. \n
\n

CONNECT报文

这是该报文的信息:

\n

\"MQTT

\n

clientID

\n

clientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端

\n

cleanSession – 清除会话

\n

表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;

\n

取值true表示保存,false表示不保存

\n
\n

注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。

\n

反之如果数据不会重要,可以取值为false

\n
\n

keepAlive —— 间隔时间

\n

用于服务端每隔多久就了解一下客户端是否与其保持连接的情况

\n

CONNACK – 确认连接请求

\"MQTT

\n

sessionPresent

\n

CONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。

\n

cleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。

\n

总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。

\n

returnCode —— 返回码

\n

当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
返回码返回码描述
0成功连接
1连接被服务端拒绝,原因是不支持客户端的MQTT协议版本
2连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。
3连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。
4连接被服务端拒绝,原因是用户名或密码无效。
5连接被服务端拒绝,原因是客户端未被授权连接到此服务端。
\n
\n

ESP8266连接MQTT服务端

http://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883

\n

接下来使用PubSubClient库(去GitHub下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。

\n
    \n
  1. 配置WiFi

    \n
  2. \n
  3. 创建mqtt客户端对象(间接创建)

    \n
    1
    2
    WiFiClient wifiClient;
    PubSubClient mqttClient(wifiClient);
    \n
  4. \n
  5. 配置客户端要连接哪个服务端(绑定服务端)

    \n
    1
    2
    3
    String mqttServer = "test.ranye-iot.net";
    const int port = 1883;
    mqttClient.setServer(mqttServer, port); //连接服务端
    \n
  6. \n
  7. 连接服务端

    \n
    1
    2
    3
    4
    //用设备的mac地址来生成唯一标识该设备的clientID
    String clientID = "esp8266-" + WiFi.macAddress();

    mqttClient.connect(clientId.c_str()) //连接成功返回true
    \n
  8. \n
\n
    \n
  1. 连接成功后保持 “心跳”,否则…(可以尝试继续连接)

    \n
    1
    2
    3
    4
    5
    6
    7
    void loop() { 
    if (mqttClient.connected()) { // 如果开发板成功连接服务器
    mqttClient.loop(); // 保持客户端心跳
    } else { // 如果开发板未能成功连接服务器
    connectMQTTServer(); // 则尝试连接服务器
    }
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "rbook";
const char* password = "12345678";
const char* mqttServer = "test.ranye-iot.net";

// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void setup() {
Serial.begin(9600);

//设置ESP8266工作模式为无线终端模式
WiFi.mode(WIFI_STA);

// 连接WiFi
connectWifi();

// 设置MQTT服务器和端口号
mqttClient.setServer(mqttServer, 1883);

// 连接MQTT服务器
connectMQTTServer();
}

void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
mqttClient.loop(); // 保持客户端心跳
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}

void connectMQTTServer(){
// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
String clientId = "esp8266-" + WiFi.macAddress();

// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}

// ESP8266连接wifi
void connectWifi(){

WiFi.begin(ssid, password);

//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

什么是MQTT

MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议

\n

优点:

\n

轻巧、开放、简单、规范,易于实现。

\n

这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。

\n

基本原理

在MQTT协议通讯中,有两个最为重要的角色。它们分别是服务端客户端

\n

服务端

\n

是消息传输的枢纽

\n

客户端

\n

客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。

\n

客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。

\n

\"image-20210810220807339\"

\n

客户端连接服务端

分2个步骤

\n
    \n
  1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息
  2. \n
  3. 服务端收到请求后,发送数据包“CONNACK”进行确认
  4. \n
\n

CONNECT报文

这是该报文的信息:

\n

\"MQTT

\n

clientID

\n

clientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端

\n

cleanSession – 清除会话

\n

表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;

\n

取值true表示保存,false表示不保存

\n
\n

注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。

\n

反之如果数据不会重要,可以取值为false

\n
\n

keepAlive —— 间隔时间

\n

用于服务端每隔多久就了解一下客户端是否与其保持连接的情况

\n

CONNACK – 确认连接请求

\"MQTT

\n

sessionPresent

\n

CONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。

\n

cleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。

\n

总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。

\n

returnCode —— 返回码

\n

当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
返回码返回码描述
0成功连接
1连接被服务端拒绝,原因是不支持客户端的MQTT协议版本
2连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。
3连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。
4连接被服务端拒绝,原因是用户名或密码无效。
5连接被服务端拒绝,原因是客户端未被授权连接到此服务端。
\n
\n

ESP8266连接MQTT服务端

http://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883

\n

接下来使用PubSubClient库(去GitHub下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。

\n
    \n
  1. 配置WiFi

    \n
  2. \n
  3. 创建mqtt客户端对象(间接创建)

    \n
    1
    2
    WiFiClient wifiClient;
    PubSubClient mqttClient(wifiClient);
    \n
  4. \n
  5. 配置客户端要连接哪个服务端(绑定服务端)

    \n
    1
    2
    3
    String mqttServer = "test.ranye-iot.net";
    const int port = 1883;
    mqttClient.setServer(mqttServer, port); //连接服务端
    \n
  6. \n
  7. 连接服务端

    \n
    1
    2
    3
    4
    //用设备的mac地址来生成唯一标识该设备的clientID
    String clientID = "esp8266-" + WiFi.macAddress();

    mqttClient.connect(clientId.c_str()) //连接成功返回true
    \n
  8. \n
\n
    \n
  1. 连接成功后保持 “心跳”,否则…(可以尝试继续连接)

    \n
    1
    2
    3
    4
    5
    6
    7
    void loop() { 
    if (mqttClient.connected()) { // 如果开发板成功连接服务器
    mqttClient.loop(); // 保持客户端心跳
    } else { // 如果开发板未能成功连接服务器
    connectMQTTServer(); // 则尝试连接服务器
    }
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "rbook";
const char* password = "12345678";
const char* mqttServer = "test.ranye-iot.net";

// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void setup() {
Serial.begin(9600);

//设置ESP8266工作模式为无线终端模式
WiFi.mode(WIFI_STA);

// 连接WiFi
connectWifi();

// 设置MQTT服务器和端口号
mqttClient.setServer(mqttServer, 1883);

// 连接MQTT服务器
connectMQTTServer();
}

void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
mqttClient.loop(); // 保持客户端心跳
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}

void connectMQTTServer(){
// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
String clientId = "esp8266-" + WiFi.macAddress();

// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}

// ESP8266连接wifi
void connectWifi(){

WiFi.begin(ssid, password);

//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}
\n"},{"title":"esp8266(8)-OTA上传代码","abbrlink":"1b6a7930","date":"2021-08-05T05:00:00.000Z","description":"esp8266如何实现无线上传代码","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。\n\n\n\n在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。\n\n## 1、通过数据线上传初始示例程序\n\n首先,请将以下示例程序通过Arduino IDE上传到ESP8266。\n\n```c\n#include \n#include \n#include \n \n// 闪烁时间间隔(秒)\nconst int blinkInterval = 2; \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"taichimaker\";\nconst char* password = \"12345678\";\n \nTicker ticker;\n \nvoid setup() {\n Serial.begin(9600); \n Serial.println(\"\");\n pinMode(LED_BUILTIN, OUTPUT);\n \n ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象\n \n connectWifi();\n \n // OTA设置并启动\n ArduinoOTA.setHostname(\"ESP8266\");\n ArduinoOTA.setPassword(\"12345678\");\n ArduinoOTA.begin();\n \n Serial.println(\"OTA ready\");\n}\nvoid loop() {\n ArduinoOTA.handle();\n}\n \n// 在Tinker对象控制下,此函数将会定时执行。\nvoid tickerCount(){\n digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));\n}\n \nvoid connectWifi(){\n //开始连接wifi\n WiFi.begin(ssid, password);\n \n //等待WiFi连接,连接成功打印IP\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.print(\"IP address:\\t\"); \n Serial.println(WiFi.localIP()); \n}\n```\n\n\n\n## 2、通过Arduino IDE正确选择OTA端口\n\n程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。\n\n如下图所示:\n\n[![esp8266 OTA 端口选择](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port-1024x547.jpg)](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port.jpg)\n\n\n\n## 3、认证并上传程序\n\n\n\n点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的`setPassword`函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。\n\n程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。\n\n\n\n## 4、OTA的局限性\n\n\n\n**1. 程序占用空间变大**\n在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。\n\n**2. Arduino IDE无法通过OTA端口与开发板进行串口通讯**\n当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。\n\n**3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi**\n若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。\n\n","source":"_posts/esp8266/8-OTA上传代码.md","raw":"---\ntitle: esp8266(8)-OTA上传代码\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 1b6a7930\ndate: 2021-08-05 13:00:00\ndescription: esp8266如何实现无线上传代码\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。\n\n\n\n在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。\n\n## 1、通过数据线上传初始示例程序\n\n首先,请将以下示例程序通过Arduino IDE上传到ESP8266。\n\n```c\n#include \n#include \n#include \n \n// 闪烁时间间隔(秒)\nconst int blinkInterval = 2; \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"taichimaker\";\nconst char* password = \"12345678\";\n \nTicker ticker;\n \nvoid setup() {\n Serial.begin(9600); \n Serial.println(\"\");\n pinMode(LED_BUILTIN, OUTPUT);\n \n ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象\n \n connectWifi();\n \n // OTA设置并启动\n ArduinoOTA.setHostname(\"ESP8266\");\n ArduinoOTA.setPassword(\"12345678\");\n ArduinoOTA.begin();\n \n Serial.println(\"OTA ready\");\n}\nvoid loop() {\n ArduinoOTA.handle();\n}\n \n// 在Tinker对象控制下,此函数将会定时执行。\nvoid tickerCount(){\n digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));\n}\n \nvoid connectWifi(){\n //开始连接wifi\n WiFi.begin(ssid, password);\n \n //等待WiFi连接,连接成功打印IP\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.print(\"IP address:\\t\"); \n Serial.println(WiFi.localIP()); \n}\n```\n\n\n\n## 2、通过Arduino IDE正确选择OTA端口\n\n程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。\n\n如下图所示:\n\n[![esp8266 OTA 端口选择](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port-1024x547.jpg)](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port.jpg)\n\n\n\n## 3、认证并上传程序\n\n\n\n点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的`setPassword`函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。\n\n程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。\n\n\n\n## 4、OTA的局限性\n\n\n\n**1. 程序占用空间变大**\n在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。\n\n**2. Arduino IDE无法通过OTA端口与开发板进行串口通讯**\n当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。\n\n**3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi**\n若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。\n\n","slug":"esp8266/8-OTA上传代码","published":1,"updated":"2021-09-11T07:55:01.650Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6py0049akve15r5eo74","content":"

所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。

\n

在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。

\n

1、通过数据线上传初始示例程序

首先,请将以下示例程序通过Arduino IDE上传到ESP8266。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <Ticker.h>

// 闪烁时间间隔(秒)
const int blinkInterval = 2;

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "taichimaker";
const char* password = "12345678";

Ticker ticker;

void setup() {
Serial.begin(9600);
Serial.println("");
pinMode(LED_BUILTIN, OUTPUT);

ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象

connectWifi();

// OTA设置并启动
ArduinoOTA.setHostname("ESP8266");
ArduinoOTA.setPassword("12345678");
ArduinoOTA.begin();

Serial.println("OTA ready");
}
void loop() {
ArduinoOTA.handle();
}

// 在Tinker对象控制下,此函数将会定时执行。
void tickerCount(){
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}

void connectWifi(){
//开始连接wifi
WiFi.begin(ssid, password);

//等待WiFi连接,连接成功打印IP
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.print("IP address:\\t");
Serial.println(WiFi.localIP());
}
\n

2、通过Arduino IDE正确选择OTA端口

程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。

\n

如下图所示:

\n

\"esp8266

\n

3、认证并上传程序

点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的setPassword函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。

\n

程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。

\n

4、OTA的局限性

1. 程序占用空间变大
在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。

\n

2. Arduino IDE无法通过OTA端口与开发板进行串口通讯
当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。

\n

3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi
若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。

\n

在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。

\n

1、通过数据线上传初始示例程序

首先,请将以下示例程序通过Arduino IDE上传到ESP8266。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <Ticker.h>

// 闪烁时间间隔(秒)
const int blinkInterval = 2;

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "taichimaker";
const char* password = "12345678";

Ticker ticker;

void setup() {
Serial.begin(9600);
Serial.println("");
pinMode(LED_BUILTIN, OUTPUT);

ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象

connectWifi();

// OTA设置并启动
ArduinoOTA.setHostname("ESP8266");
ArduinoOTA.setPassword("12345678");
ArduinoOTA.begin();

Serial.println("OTA ready");
}
void loop() {
ArduinoOTA.handle();
}

// 在Tinker对象控制下,此函数将会定时执行。
void tickerCount(){
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}

void connectWifi(){
//开始连接wifi
WiFi.begin(ssid, password);

//等待WiFi连接,连接成功打印IP
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.print("IP address:\\t");
Serial.println(WiFi.localIP());
}
\n

2、通过Arduino IDE正确选择OTA端口

程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。

\n

如下图所示:

\n

\"esp8266

\n

3、认证并上传程序

点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的setPassword函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。

\n

程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。

\n

4、OTA的局限性

1. 程序占用空间变大
在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。

\n

2. Arduino IDE无法通过OTA端口与开发板进行串口通讯
当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。

\n

3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi
若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。

\n"},{"title":"esp8266(7)-多任务执行","abbrlink":"c25015c5","date":"2021-08-04T01:00:00.000Z","description":"esp8266如何同时执行多个任务","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n通过Ticker库可以让esp8266同时执行多个任务\n\n\n\n## 1、如何实现\n\n示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息\n\n1. 创建 `Ticker`对象\n\n ```c\n Ticker ticker;\n ```\n\n2. 定时执行某个函数\n\n ```c\n ticker.attach(time, func); //time单位为s,func为函数名\n ticker.attach_ms(time, func); //time单位为ms,func为函数名\n ```\n\n \n\n```c\n#include \n\nTicker ticker;// 建立Ticker用于实现定时功能\nint count; // 计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n pinMode(LED_BUILTIN, OUTPUT);\n\n // attach函数的第一个参数是控制定时间隔的变量。该参数的单位为秒。第二个参数是\n // 定时执行的函数名称。\n ticker.attach(1, sayHi);\n}\n\nvoid loop() {\n // 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务\n for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n\n for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n delay(3000);\n}\n\n// 在Tinker对象控制下,此函数将会定时执行。\nvoid sayHi(){\n count++;\n Serial.print(\"Hi \");\n Serial.println(count);\n}\n```\n\n\n\n## 2、其他操作\n\n\n\n- 停止执行定时任务:ticker.detach()\n\n- 向定时调用函数传递参数:ticker.attach(1, sayHi, 8)\n\n 【注意】:\n\n attach函数所能传递的参数最多只有一个\n\n 该参数仅能是以下类型中的一种:char, short, int, float, void\\*, char\\*\n\n- **利用多个Ticker对象可以让ESP8266处理多任务**\n\n\n\n","source":"_posts/esp8266/7-多任务执行.md","raw":"---\ntitle: esp8266(7)-多任务执行\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: c25015c5\ndate: 2021-08-04 09:00:00\ndescription: esp8266如何同时执行多个任务\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n通过Ticker库可以让esp8266同时执行多个任务\n\n\n\n## 1、如何实现\n\n示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息\n\n1. 创建 `Ticker`对象\n\n ```c\n Ticker ticker;\n ```\n\n2. 定时执行某个函数\n\n ```c\n ticker.attach(time, func); //time单位为s,func为函数名\n ticker.attach_ms(time, func); //time单位为ms,func为函数名\n ```\n\n \n\n```c\n#include \n\nTicker ticker;// 建立Ticker用于实现定时功能\nint count; // 计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n pinMode(LED_BUILTIN, OUTPUT);\n\n // attach函数的第一个参数是控制定时间隔的变量。该参数的单位为秒。第二个参数是\n // 定时执行的函数名称。\n ticker.attach(1, sayHi);\n}\n\nvoid loop() {\n // 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务\n for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n\n for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n delay(3000);\n}\n\n// 在Tinker对象控制下,此函数将会定时执行。\nvoid sayHi(){\n count++;\n Serial.print(\"Hi \");\n Serial.println(count);\n}\n```\n\n\n\n## 2、其他操作\n\n\n\n- 停止执行定时任务:ticker.detach()\n\n- 向定时调用函数传递参数:ticker.attach(1, sayHi, 8)\n\n 【注意】:\n\n attach函数所能传递的参数最多只有一个\n\n 该参数仅能是以下类型中的一种:char, short, int, float, void\\*, char\\*\n\n- **利用多个Ticker对象可以让ESP8266处理多任务**\n\n\n\n","slug":"esp8266/7-多任务执行","published":1,"updated":"2021-09-11T07:54:50.150Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6py004cakve0g6zci7t","content":"

通过Ticker库可以让esp8266同时执行多个任务

\n

1、如何实现

示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息

\n
    \n
  1. 创建 Ticker对象

    \n
    1
    Ticker ticker;
    \n
  2. \n
  3. 定时执行某个函数

    \n
    1
    2
    ticker.attach(time, func);   //time单位为s,func为函数名
    ticker.attach_ms(time, func); //time单位为ms,func为函数名
    \n
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <Ticker.h>

Ticker ticker;// 建立Ticker用于实现定时功能
int count; // 计数用变量

void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);

// attach函数的第一个参数是控制定时间隔的变量。该参数的单位为秒。第二个参数是
// 定时执行的函数名称。
ticker.attach(1, sayHi);
}

void loop() {
// 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务
for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {
analogWrite(LED_BUILTIN, fadeValue);
delay(10);
}

for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {
analogWrite(LED_BUILTIN, fadeValue);
delay(10);
}
delay(3000);
}

// 在Tinker对象控制下,此函数将会定时执行。
void sayHi(){
count++;
Serial.print("Hi ");
Serial.println(count);
}
\n

2、其他操作

    \n
  • 停止执行定时任务:ticker.detach()

    \n
  • \n
  • 向定时调用函数传递参数:ticker.attach(1, sayHi, 8)

    \n

    【注意】:

    \n

    attach函数所能传递的参数最多只有一个

    \n

    该参数仅能是以下类型中的一种:char, short, int, float, void*, char*

    \n
  • \n
  • 利用多个Ticker对象可以让ESP8266处理多任务

    \n
  • \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

通过Ticker库可以让esp8266同时执行多个任务

\n

1、如何实现

示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息

\n
    \n
  1. 创建 Ticker对象

    \n
    1
    Ticker ticker;
    \n
  2. \n
  3. 定时执行某个函数

    \n
    1
    2
    ticker.attach(time, func);   //time单位为s,func为函数名
    ticker.attach_ms(time, func); //time单位为ms,func为函数名
    \n
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <Ticker.h>

Ticker ticker;// 建立Ticker用于实现定时功能
int count; // 计数用变量

void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);

// attach函数的第一个参数是控制定时间隔的变量。该参数的单位为秒。第二个参数是
// 定时执行的函数名称。
ticker.attach(1, sayHi);
}

void loop() {
// 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务
for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {
analogWrite(LED_BUILTIN, fadeValue);
delay(10);
}

for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {
analogWrite(LED_BUILTIN, fadeValue);
delay(10);
}
delay(3000);
}

// 在Tinker对象控制下,此函数将会定时执行。
void sayHi(){
count++;
Serial.print("Hi ");
Serial.println(count);
}
\n

2、其他操作

    \n
  • 停止执行定时任务:ticker.detach()

    \n
  • \n
  • 向定时调用函数传递参数:ticker.attach(1, sayHi, 8)

    \n

    【注意】:

    \n

    attach函数所能传递的参数最多只有一个

    \n

    该参数仅能是以下类型中的一种:char, short, int, float, void*, char*

    \n
  • \n
  • 利用多个Ticker对象可以让ESP8266处理多任务

    \n
  • \n
\n"},{"title":"esp8266(6)-WiFi配网","abbrlink":"baad5185","date":"2021-08-04T00:00:00.000Z","description":"使用手机等终端设备辅助esp8266进行联网","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?\n\n这里主要用到 `WiFiManager`库,可以去太极创客[下载](https://github.com/taichi-maker/WiFiManager/archive/refs/heads/master.zip)并加载到ArduinoIDE中。\n\n\n\n## 1、清除WiFi信息\n\n使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了\n\n只需2个步骤:\n\n1. 创建WiFiManager对象\n\n2. 清除WiFi信息\n\n ```c\n WiFiManager wifiManager;\t\t//创建对象\n wifiManager.resetSettings();\t//清除WiFi信息\n ```\n\n完整code\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果\n wifiManager.resetSettings();\n Serial.println(\"ESP8266 WiFi Settings Cleared\");\n}\n \nvoid loop() {}\n```\n\n\n\n\n\n## 2、WiFi配网\n\n1. 创建WiFiManager对象\n\n2. 开启接入点模式(就是在开发板上开个热点,让周围设备连接)\n\n ```c\n wifiManager.autoConnect(\"热点名称\"[, \"密码\"]);\n ```\n\n3. 接下来就可以用手机、电脑配网\n\n (1)用手机(或电脑)连接开发板的热点\n\n (2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi\n\n (3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网\n\n (4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要[擦除已连接的WiFi信息](#1、清除WiFi信息)\n\n (5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。\n\n4. 判断是否配网成功\n\n 可以在串口监视屏打印连接上的WiFi名称\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称\n wifiManager.autoConnect(\"ESP8266-wifi\");\n \n // 如果您希望该WiFi添加密码,可以使用以下语句:\n // wifiManager.autoConnect(\"ESP8266-wifi\", \"12345678\");\n \n \n // WiFi连接成功后将通过串口监视器输出连接成功信息 \n Serial.println(\"\"); \n Serial.print(\"ESP8266 Connected to \");\n Serial.println(WiFi.SSID()); // 连接上的WiFi名称\n Serial.print(\"IP address:\\t\");\n Serial.println(WiFi.localIP()); // IP\n}\n \nvoid loop() {}\n```\n\n![image-20210811095227391](https://gitee.com/ajream/images/raw/master/img/2021-08-1109-56-45_image-20210811095227391.png)\n\n","source":"_posts/esp8266/6-WiFi配网.md","raw":"---\ntitle: esp8266(6)-WiFi配网\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: baad5185\ndate: 2021-08-04 08:00:00\ndescription: 使用手机等终端设备辅助esp8266进行联网\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?\n\n这里主要用到 `WiFiManager`库,可以去太极创客[下载](https://github.com/taichi-maker/WiFiManager/archive/refs/heads/master.zip)并加载到ArduinoIDE中。\n\n\n\n## 1、清除WiFi信息\n\n使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了\n\n只需2个步骤:\n\n1. 创建WiFiManager对象\n\n2. 清除WiFi信息\n\n ```c\n WiFiManager wifiManager;\t\t//创建对象\n wifiManager.resetSettings();\t//清除WiFi信息\n ```\n\n完整code\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果\n wifiManager.resetSettings();\n Serial.println(\"ESP8266 WiFi Settings Cleared\");\n}\n \nvoid loop() {}\n```\n\n\n\n\n\n## 2、WiFi配网\n\n1. 创建WiFiManager对象\n\n2. 开启接入点模式(就是在开发板上开个热点,让周围设备连接)\n\n ```c\n wifiManager.autoConnect(\"热点名称\"[, \"密码\"]);\n ```\n\n3. 接下来就可以用手机、电脑配网\n\n (1)用手机(或电脑)连接开发板的热点\n\n (2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi\n\n (3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网\n\n (4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要[擦除已连接的WiFi信息](#1、清除WiFi信息)\n\n (5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。\n\n4. 判断是否配网成功\n\n 可以在串口监视屏打印连接上的WiFi名称\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称\n wifiManager.autoConnect(\"ESP8266-wifi\");\n \n // 如果您希望该WiFi添加密码,可以使用以下语句:\n // wifiManager.autoConnect(\"ESP8266-wifi\", \"12345678\");\n \n \n // WiFi连接成功后将通过串口监视器输出连接成功信息 \n Serial.println(\"\"); \n Serial.print(\"ESP8266 Connected to \");\n Serial.println(WiFi.SSID()); // 连接上的WiFi名称\n Serial.print(\"IP address:\\t\");\n Serial.println(WiFi.localIP()); // IP\n}\n \nvoid loop() {}\n```\n\n![image-20210811095227391](https://gitee.com/ajream/images/raw/master/img/2021-08-1109-56-45_image-20210811095227391.png)\n\n","slug":"esp8266/6-WiFi配网","published":1,"updated":"2021-09-11T07:54:46.084Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q0004eakve6dzd49z7","content":"

之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?

\n

这里主要用到 WiFiManager库,可以去太极创客下载并加载到ArduinoIDE中。

\n

1、清除WiFi信息

使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了

\n

只需2个步骤:

\n
    \n
  1. 创建WiFiManager对象

    \n
  2. \n
  3. 清除WiFi信息

    \n
    1
    2
    WiFiManager wifiManager;\t\t//创建对象
    wifiManager.resetSettings();\t//清除WiFi信息
    \n
  4. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <ESP8266WiFi.h>          
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

void setup() {
Serial.begin(9600);
// 建立WiFiManager对象
WiFiManager wifiManager;

// 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果
wifiManager.resetSettings();
Serial.println("ESP8266 WiFi Settings Cleared");
}

void loop() {}
\n

2、WiFi配网

    \n
  1. 创建WiFiManager对象

    \n
  2. \n
  3. 开启接入点模式(就是在开发板上开个热点,让周围设备连接)

    \n
    1
    wifiManager.autoConnect("热点名称"[, "密码"]);
    \n
  4. \n
  5. 接下来就可以用手机、电脑配网

    \n

    (1)用手机(或电脑)连接开发板的热点

    \n

    (2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi

    \n

    (3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网

    \n

    (4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要擦除已连接的WiFi信息

    \n

    (5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。

    \n
  6. \n
  7. 判断是否配网成功

    \n

    可以在串口监视屏打印连接上的WiFi名称

    \n
  8. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <ESP8266WiFi.h>          
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

void setup() {
Serial.begin(9600);
// 建立WiFiManager对象
WiFiManager wifiManager;

// 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称
wifiManager.autoConnect("ESP8266-wifi");

// 如果您希望该WiFi添加密码,可以使用以下语句:
// wifiManager.autoConnect("ESP8266-wifi", "12345678");


// WiFi连接成功后将通过串口监视器输出连接成功信息
Serial.println("");
Serial.print("ESP8266 Connected to ");
Serial.println(WiFi.SSID()); // 连接上的WiFi名称
Serial.print("IP address:\\t");
Serial.println(WiFi.localIP()); // IP
}

void loop() {}
\n

\"image-20210811095227391\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?

\n

这里主要用到 WiFiManager库,可以去太极创客下载并加载到ArduinoIDE中。

\n

1、清除WiFi信息

使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了

\n

只需2个步骤:

\n
    \n
  1. 创建WiFiManager对象

    \n
  2. \n
  3. 清除WiFi信息

    \n
    1
    2
    WiFiManager wifiManager;\t\t//创建对象
    wifiManager.resetSettings();\t//清除WiFi信息
    \n
  4. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <ESP8266WiFi.h>          
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

void setup() {
Serial.begin(9600);
// 建立WiFiManager对象
WiFiManager wifiManager;

// 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果
wifiManager.resetSettings();
Serial.println("ESP8266 WiFi Settings Cleared");
}

void loop() {}
\n

2、WiFi配网

    \n
  1. 创建WiFiManager对象

    \n
  2. \n
  3. 开启接入点模式(就是在开发板上开个热点,让周围设备连接)

    \n
    1
    wifiManager.autoConnect("热点名称"[, "密码"]);
    \n
  4. \n
  5. 接下来就可以用手机、电脑配网

    \n

    (1)用手机(或电脑)连接开发板的热点

    \n

    (2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi

    \n

    (3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网

    \n

    (4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要擦除已连接的WiFi信息

    \n

    (5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。

    \n
  6. \n
  7. 判断是否配网成功

    \n

    可以在串口监视屏打印连接上的WiFi名称

    \n
  8. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <ESP8266WiFi.h>          
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

void setup() {
Serial.begin(9600);
// 建立WiFiManager对象
WiFiManager wifiManager;

// 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称
wifiManager.autoConnect("ESP8266-wifi");

// 如果您希望该WiFi添加密码,可以使用以下语句:
// wifiManager.autoConnect("ESP8266-wifi", "12345678");


// WiFi连接成功后将通过串口监视器输出连接成功信息
Serial.println("");
Serial.print("ESP8266 Connected to ");
Serial.println(WiFi.SSID()); // 连接上的WiFi名称
Serial.print("IP address:\\t");
Serial.println(WiFi.localIP()); // IP
}

void loop() {}
\n

\"image-20210811095227391\"

\n"},{"title":"MQTT篇(三)关于MQTT的主题","abbrlink":"894e18ee","date":"2021-08-11T04:01:07.000Z","description":"客户端向服务端发布的消息类型、或者要从服务端订阅的消息类型,就是一个个主题,主题在实际应用中用一个字符串来表示","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n## 主题\n\n### 主题基本形式\n\n​\t\t主题基本形式就是一个字符串,比如:currentTime,carSpeed\n\n​\t\t注意:主题区分大小写、可以用空格(但尽量不要用)、尽量使用英文字符\n\n\n\n### 主题分级\n\n​\t\tMQTT主题各个级别之间可以使用`/`来分隔。如:`Tyler-1/motor/1/speed`\n\n​\t\t示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。\n\n\n\n### 主题通配符\n\n​\t\t当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用\n\n- 单级通配符: `+`\t\t示例:\n\n ```sh\n home/sensor/+/temperature\n \n # 可以表示:\n # home/sensor/aaa/temperature\n # home/sensor/bbb/temperature\n # ......\n ```\n\n \n\n- 多级通配符 :`#` 示例:\n\n ```sh\n home/sensor/#\n \n # 可以表示:\n # home/sensor/aaa\n # home/sensor/bbb/ccc\n # home/sensor/aaa/ccc\n ```\n\n \n\n### 注意事项\n\n- 以$开始的主题\n\n 以$开始的主题是MQTT服务端**系统保留的特殊主题**,不能随意订阅或者向其发布信息。如:\n\n ```shell\n $SYS/broker/clients/connected\n $SYS/broker/clients/disconnected\n $SYS/broker/clients/total\n $SYS/broker/messages/sent\n $SYS/broker/uptime\n ...\n ```\n\n- 避免使用`/`作为主题的开头\n\n- 主题中尽量**不要使用空格**\n\n- 主题中尽量使用**ASCII字符**\n\n- 建议在主题中嵌入客户端ID\n 【通过主题中的客户端ID内容,可以很容易的了解该主题信息是由哪一台设备所发布的】\n\n \n\n## ESP8266订阅主题\n\n","source":"_posts/esp8266/MQTT篇(三)-主题.md","raw":"---\ntitle: MQTT篇(三)关于MQTT的主题\ntags:\n - 物联网\n - esp8266\ncategories:\n - 硬件学习\n - 物联网开发\nabbrlink: 894e18ee\ndate: 2021-08-11 12:01:07\ndescription: 客户端向服务端发布的消息类型、或者要从服务端订阅的消息类型,就是一个个主题,主题在实际应用中用一个字符串来表示\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n## 主题\n\n### 主题基本形式\n\n​\t\t主题基本形式就是一个字符串,比如:currentTime,carSpeed\n\n​\t\t注意:主题区分大小写、可以用空格(但尽量不要用)、尽量使用英文字符\n\n\n\n### 主题分级\n\n​\t\tMQTT主题各个级别之间可以使用`/`来分隔。如:`Tyler-1/motor/1/speed`\n\n​\t\t示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。\n\n\n\n### 主题通配符\n\n​\t\t当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用\n\n- 单级通配符: `+`\t\t示例:\n\n ```sh\n home/sensor/+/temperature\n \n # 可以表示:\n # home/sensor/aaa/temperature\n # home/sensor/bbb/temperature\n # ......\n ```\n\n \n\n- 多级通配符 :`#` 示例:\n\n ```sh\n home/sensor/#\n \n # 可以表示:\n # home/sensor/aaa\n # home/sensor/bbb/ccc\n # home/sensor/aaa/ccc\n ```\n\n \n\n### 注意事项\n\n- 以$开始的主题\n\n 以$开始的主题是MQTT服务端**系统保留的特殊主题**,不能随意订阅或者向其发布信息。如:\n\n ```shell\n $SYS/broker/clients/connected\n $SYS/broker/clients/disconnected\n $SYS/broker/clients/total\n $SYS/broker/messages/sent\n $SYS/broker/uptime\n ...\n ```\n\n- 避免使用`/`作为主题的开头\n\n- 主题中尽量**不要使用空格**\n\n- 主题中尽量使用**ASCII字符**\n\n- 建议在主题中嵌入客户端ID\n 【通过主题中的客户端ID内容,可以很容易的了解该主题信息是由哪一台设备所发布的】\n\n \n\n## ESP8266订阅主题\n\n","slug":"esp8266/MQTT篇(三)-主题","published":1,"updated":"2021-08-29T14:54:46.346Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q1004hakve7ci71f3m","content":"

主题

主题基本形式

​ 主题基本形式就是一个字符串,比如:currentTime,carSpeed

\n

​ 注意:主题区分大小写、可以用空格(但尽量不要用)、尽量使用英文字符

\n

主题分级

​ MQTT主题各个级别之间可以使用/来分隔。如:Tyler-1/motor/1/speed

\n

​ 示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。

\n

主题通配符

​ 当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用

\n
    \n
  • 单级通配符: + 示例:

    \n
    1
    2
    3
    4
    5
    6
    home/sensor/+/temperature

    # 可以表示:
    # home/sensor/aaa/temperature
    # home/sensor/bbb/temperature
    # ......
    \n
  • \n
\n
    \n
  • 多级通配符 :# 示例:

    \n
    1
    2
    3
    4
    5
    6
    home/sensor/#

    # 可以表示:
    # home/sensor/aaa
    # home/sensor/bbb/ccc
    # home/sensor/aaa/ccc
    \n
  • \n
\n

注意事项

    \n
  • 以$开始的主题

    \n

    以$开始的主题是MQTT服务端系统保留的特殊主题,不能随意订阅或者向其发布信息。如:

    \n
    1
    2
    3
    4
    5
    6
    $SYS/broker/clients/connected
    $SYS/broker/clients/disconnected
    $SYS/broker/clients/total
    $SYS/broker/messages/sent
    $SYS/broker/uptime
    ...
    \n
  • \n
  • 避免使用/作为主题的开头

    \n
  • \n
  • 主题中尽量不要使用空格

    \n
  • \n
  • 主题中尽量使用ASCII字符

    \n
  • \n
  • 建议在主题中嵌入客户端ID
    【通过主题中的客户端ID内容,可以很容易的了解该主题信息是由哪一台设备所发布的】

    \n
  • \n
\n

ESP8266订阅主题

","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

主题

主题基本形式

​ 主题基本形式就是一个字符串,比如:currentTime,carSpeed

\n

​ 注意:主题区分大小写、可以用空格(但尽量不要用)、尽量使用英文字符

\n

主题分级

​ MQTT主题各个级别之间可以使用/来分隔。如:Tyler-1/motor/1/speed

\n

​ 示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。

\n

主题通配符

​ 当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用

\n
    \n
  • 单级通配符: + 示例:

    \n
    1
    2
    3
    4
    5
    6
    home/sensor/+/temperature

    # 可以表示:
    # home/sensor/aaa/temperature
    # home/sensor/bbb/temperature
    # ......
    \n
  • \n
\n
    \n
  • 多级通配符 :# 示例:

    \n
    1
    2
    3
    4
    5
    6
    home/sensor/#

    # 可以表示:
    # home/sensor/aaa
    # home/sensor/bbb/ccc
    # home/sensor/aaa/ccc
    \n
  • \n
\n

注意事项

    \n
  • 以$开始的主题

    \n

    以$开始的主题是MQTT服务端系统保留的特殊主题,不能随意订阅或者向其发布信息。如:

    \n
    1
    2
    3
    4
    5
    6
    $SYS/broker/clients/connected
    $SYS/broker/clients/disconnected
    $SYS/broker/clients/total
    $SYS/broker/messages/sent
    $SYS/broker/uptime
    ...
    \n
  • \n
  • 避免使用/作为主题的开头

    \n
  • \n
  • 主题中尽量不要使用空格

    \n
  • \n
  • 主题中尽量使用ASCII字符

    \n
  • \n
  • 建议在主题中嵌入客户端ID
    【通过主题中的客户端ID内容,可以很容易的了解该主题信息是由哪一台设备所发布的】

    \n
  • \n
\n

ESP8266订阅主题

"},{"title":"MQTT篇(二)消息的发布、订阅与取消订阅","abbrlink":"adfcabaa","date":"2021-08-10T02:20:32.000Z","description":"MQTT协议指明了客户端与服务端之间信息交流的方式","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n## 发布信息\n\n1. mqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题\n2. 服务端根据主题决定将信息转发给哪些客户端\n\n\n\n![PUBLISH - 发布消息](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-PUBLISH.png)\n\n\n\n发布信息时,会向服务端发送一个**PUBLISH报文**:\n\n![MQTT PUBLISH 报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-Publish-Packet.png)\n\ntopicName:主题名\n\nqos:服务质量等级,分0、1、2共三个等级\n\npacketId:报文标识符,用于区别不同报文\n\n​\t\t【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。\n\nretainFlag:保留标志。\n\n​\t\t一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;\n\n​\t\t特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息\n\n\n\npayLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的\n\n\n\ndupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。\n\n【注意】重发标志只在QoS级别大于0时使用。\n\n## 订阅主题\n\n当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。\n\n客户端是通过向服务端发送**SUBSCRIBE报文**来实现订阅主题\n\n【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。\n\nqos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。\n\nMQTT设备可以通过**“报文标识符”**对MQTT报文进行甄别和管理。\n\n---\n\n**订阅确认:**\n\n服务端接收到客户端的订阅报文后,会向客户端发送**SUBACK报文**确认订阅。\n\n该报文包含**”订阅返回码“**和**”报文标识符“**两个信息\n\n| 返回码 | Return Code Response |\n| :----- | :------------------- |\n| 0 | 订阅成功 – QoS 0 |\n| 1 | 订阅成功- QoS 1 |\n| 2 | 订阅成功- QoS 2 |\n| 128 | 订阅失败 |\n\n---\n\n【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。\n\n**报文标识符**:MQTT设备可以通过该标识符对报文进行管理。\n\n\n\n## 取消订阅\n\n客户端要取消订阅某主题时,可通过向服务端发送**UNSUBSCRIBE** 报文来实现。\n\n![MQTT-UNSUBSCRIBE-取消订阅报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/unsubscribe_packet.png)\n\nUNSUBSCRIBE报文包含两个重要信息:\n\n1. packetId:“报文标识符”\n2. topic1、topic2、…… :取消订阅的主题的名称\n\n\n\n当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。\n\n客户端接收到UNSUBACK报文后就可以确认取消主题订阅\n\n\n\n## ESP8266发布MQTT消息\n\n```c\n#include \n#include \n#include \n\n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"RBook\";\nconst char* password = \"1719171945678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n\n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n\nTicker ticker;\nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n\nint count; // Ticker计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n\n // Ticker定时对象\n ticker.attach(1, tickerCount); \n}\n\nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器\n // 每隔3秒钟发布一次信息\n if (count >= 3){\n pubMQTTmsg();\n count = 0;\n } \n // 保持心跳\n mqttClient.loop();\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n\nvoid tickerCount(){\n count++;\n}\n\nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// 发布信息\nvoid pubMQTTmsg(){\n static int value; // 客户端发布信息用数字\n\n // 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。\n // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,\n String topicString = \"Taichi-Maker-Pub-\" + WiFi.macAddress();\n char publishTopic[topicString.length() + 1]; \n strcpy(publishTopic, topicString.c_str());\n\n // 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。\n String messageString = \"Hello World \" + String(value++); \n char publishMsg[messageString.length() + 1]; \n strcpy(publishMsg, messageString.c_str());\n\n // 实现ESP8266向主题发布信息\n if(mqttClient.publish(publishTopic, publishMsg)){\n Serial.println(\"Publish Topic:\");Serial.println(publishTopic);\n Serial.println(\"Publish message:\");Serial.println(publishMsg); \n } else {\n Serial.println(\"Message Publish Failed.\"); \n }\n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","source":"_posts/esp8266/MQTT篇(二)-发布、订阅、取消订阅.md","raw":"---\ntitle: MQTT篇(二)消息的发布、订阅与取消订阅\ntags:\n - 物联网\n - esp8266\ncategories:\n - - 硬件学习\n - 物联网开发\nabbrlink: adfcabaa\ndate: 2021-08-10 10:20:32\ndescription: MQTT协议指明了客户端与服务端之间信息交流的方式\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n## 发布信息\n\n1. mqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题\n2. 服务端根据主题决定将信息转发给哪些客户端\n\n\n\n![PUBLISH - 发布消息](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-PUBLISH.png)\n\n\n\n发布信息时,会向服务端发送一个**PUBLISH报文**:\n\n![MQTT PUBLISH 报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-Publish-Packet.png)\n\ntopicName:主题名\n\nqos:服务质量等级,分0、1、2共三个等级\n\npacketId:报文标识符,用于区别不同报文\n\n​\t\t【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。\n\nretainFlag:保留标志。\n\n​\t\t一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;\n\n​\t\t特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息\n\n\n\npayLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的\n\n\n\ndupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。\n\n【注意】重发标志只在QoS级别大于0时使用。\n\n## 订阅主题\n\n当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。\n\n客户端是通过向服务端发送**SUBSCRIBE报文**来实现订阅主题\n\n【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。\n\nqos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。\n\nMQTT设备可以通过**“报文标识符”**对MQTT报文进行甄别和管理。\n\n---\n\n**订阅确认:**\n\n服务端接收到客户端的订阅报文后,会向客户端发送**SUBACK报文**确认订阅。\n\n该报文包含**”订阅返回码“**和**”报文标识符“**两个信息\n\n| 返回码 | Return Code Response |\n| :----- | :------------------- |\n| 0 | 订阅成功 – QoS 0 |\n| 1 | 订阅成功- QoS 1 |\n| 2 | 订阅成功- QoS 2 |\n| 128 | 订阅失败 |\n\n---\n\n【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。\n\n**报文标识符**:MQTT设备可以通过该标识符对报文进行管理。\n\n\n\n## 取消订阅\n\n客户端要取消订阅某主题时,可通过向服务端发送**UNSUBSCRIBE** 报文来实现。\n\n![MQTT-UNSUBSCRIBE-取消订阅报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/unsubscribe_packet.png)\n\nUNSUBSCRIBE报文包含两个重要信息:\n\n1. packetId:“报文标识符”\n2. topic1、topic2、…… :取消订阅的主题的名称\n\n\n\n当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。\n\n客户端接收到UNSUBACK报文后就可以确认取消主题订阅\n\n\n\n## ESP8266发布MQTT消息\n\n```c\n#include \n#include \n#include \n\n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"RBook\";\nconst char* password = \"1719171945678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n\n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n\nTicker ticker;\nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n\nint count; // Ticker计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n\n // Ticker定时对象\n ticker.attach(1, tickerCount); \n}\n\nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器\n // 每隔3秒钟发布一次信息\n if (count >= 3){\n pubMQTTmsg();\n count = 0;\n } \n // 保持心跳\n mqttClient.loop();\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n\nvoid tickerCount(){\n count++;\n}\n\nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// 发布信息\nvoid pubMQTTmsg(){\n static int value; // 客户端发布信息用数字\n\n // 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。\n // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,\n String topicString = \"Taichi-Maker-Pub-\" + WiFi.macAddress();\n char publishTopic[topicString.length() + 1]; \n strcpy(publishTopic, topicString.c_str());\n\n // 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。\n String messageString = \"Hello World \" + String(value++); \n char publishMsg[messageString.length() + 1]; \n strcpy(publishMsg, messageString.c_str());\n\n // 实现ESP8266向主题发布信息\n if(mqttClient.publish(publishTopic, publishMsg)){\n Serial.println(\"Publish Topic:\");Serial.println(publishTopic);\n Serial.println(\"Publish message:\");Serial.println(publishMsg); \n } else {\n Serial.println(\"Message Publish Failed.\"); \n }\n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","slug":"esp8266/MQTT篇(二)-发布、订阅、取消订阅","published":1,"updated":"2021-08-29T14:54:42.648Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q2004jakvedojmd1vx","content":"

发布信息

    \n
  1. mqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题
  2. \n
  3. 服务端根据主题决定将信息转发给哪些客户端
  4. \n
\n

\"PUBLISH

\n

发布信息时,会向服务端发送一个PUBLISH报文

\n

\"MQTT

\n

topicName:主题名

\n

qos:服务质量等级,分0、1、2共三个等级

\n

packetId:报文标识符,用于区别不同报文

\n

​ 【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。

\n

retainFlag:保留标志。

\n

​ 一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;

\n

​ 特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息

\n

payLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的

\n

dupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。

\n

【注意】重发标志只在QoS级别大于0时使用。

\n

订阅主题

当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。

\n

客户端是通过向服务端发送SUBSCRIBE报文来实现订阅主题

\n

【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。

\n

qos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。

\n

MQTT设备可以通过“报文标识符”对MQTT报文进行甄别和管理。

\n
\n

订阅确认:

\n

服务端接收到客户端的订阅报文后,会向客户端发送SUBACK报文确认订阅。

\n

该报文包含”订阅返回码“”报文标识符“两个信息

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
返回码Return Code Response
0订阅成功 – QoS 0
1订阅成功- QoS 1
2订阅成功- QoS 2
128订阅失败
\n
\n
\n

【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。

\n

报文标识符:MQTT设备可以通过该标识符对报文进行管理。

\n

取消订阅

客户端要取消订阅某主题时,可通过向服务端发送UNSUBSCRIBE 报文来实现。

\n

\"MQTT-UNSUBSCRIBE-取消订阅报文\"

\n

UNSUBSCRIBE报文包含两个重要信息:

\n
    \n
  1. packetId:“报文标识符”
  2. \n
  3. topic1、topic2、…… :取消订阅的主题的名称
  4. \n
\n

当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。

\n

客户端接收到UNSUBACK报文后就可以确认取消主题订阅

\n

ESP8266发布MQTT消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "RBook";
const char* password = "1719171945678";
const char* mqttServer = "test.ranye-iot.net";

// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

Ticker ticker;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

int count; // Ticker计数用变量

void setup() {
Serial.begin(9600);

//设置ESP8266工作模式为无线终端模式
WiFi.mode(WIFI_STA);

// 连接WiFi
connectWifi();

// 设置MQTT服务器和端口号
mqttClient.setServer(mqttServer, 1883);

// 连接MQTT服务器
connectMQTTServer();

// Ticker定时对象
ticker.attach(1, tickerCount);
}

void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
// 每隔3秒钟发布一次信息
if (count >= 3){
pubMQTTmsg();
count = 0;
}
// 保持心跳
mqttClient.loop();
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}

void tickerCount(){
count++;
}

void connectMQTTServer(){
// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
String clientId = "esp8266-" + WiFi.macAddress();

// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}

// 发布信息
void pubMQTTmsg(){
static int value; // 客户端发布信息用数字

// 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。
// 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,
String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress();
char publishTopic[topicString.length() + 1];
strcpy(publishTopic, topicString.c_str());

// 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。
String messageString = "Hello World " + String(value++);
char publishMsg[messageString.length() + 1];
strcpy(publishMsg, messageString.c_str());

// 实现ESP8266向主题发布信息
if(mqttClient.publish(publishTopic, publishMsg)){
Serial.println("Publish Topic:");Serial.println(publishTopic);
Serial.println("Publish message:");Serial.println(publishMsg);
} else {
Serial.println("Message Publish Failed.");
}
}

// ESP8266连接wifi
void connectWifi(){

WiFi.begin(ssid, password);

//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

发布信息

    \n
  1. mqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题
  2. \n
  3. 服务端根据主题决定将信息转发给哪些客户端
  4. \n
\n

\"PUBLISH

\n

发布信息时,会向服务端发送一个PUBLISH报文

\n

\"MQTT

\n

topicName:主题名

\n

qos:服务质量等级,分0、1、2共三个等级

\n

packetId:报文标识符,用于区别不同报文

\n

​ 【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。

\n

retainFlag:保留标志。

\n

​ 一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;

\n

​ 特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息

\n

payLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的

\n

dupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。

\n

【注意】重发标志只在QoS级别大于0时使用。

\n

订阅主题

当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。

\n

客户端是通过向服务端发送SUBSCRIBE报文来实现订阅主题

\n

【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。

\n

qos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。

\n

MQTT设备可以通过“报文标识符”对MQTT报文进行甄别和管理。

\n
\n

订阅确认:

\n

服务端接收到客户端的订阅报文后,会向客户端发送SUBACK报文确认订阅。

\n

该报文包含”订阅返回码“”报文标识符“两个信息

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
返回码Return Code Response
0订阅成功 – QoS 0
1订阅成功- QoS 1
2订阅成功- QoS 2
128订阅失败
\n
\n
\n

【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。

\n

报文标识符:MQTT设备可以通过该标识符对报文进行管理。

\n

取消订阅

客户端要取消订阅某主题时,可通过向服务端发送UNSUBSCRIBE 报文来实现。

\n

\"MQTT-UNSUBSCRIBE-取消订阅报文\"

\n

UNSUBSCRIBE报文包含两个重要信息:

\n
    \n
  1. packetId:“报文标识符”
  2. \n
  3. topic1、topic2、…… :取消订阅的主题的名称
  4. \n
\n

当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。

\n

客户端接收到UNSUBACK报文后就可以确认取消主题订阅

\n

ESP8266发布MQTT消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "RBook";
const char* password = "1719171945678";
const char* mqttServer = "test.ranye-iot.net";

// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

Ticker ticker;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

int count; // Ticker计数用变量

void setup() {
Serial.begin(9600);

//设置ESP8266工作模式为无线终端模式
WiFi.mode(WIFI_STA);

// 连接WiFi
connectWifi();

// 设置MQTT服务器和端口号
mqttClient.setServer(mqttServer, 1883);

// 连接MQTT服务器
connectMQTTServer();

// Ticker定时对象
ticker.attach(1, tickerCount);
}

void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
// 每隔3秒钟发布一次信息
if (count >= 3){
pubMQTTmsg();
count = 0;
}
// 保持心跳
mqttClient.loop();
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}

void tickerCount(){
count++;
}

void connectMQTTServer(){
// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
String clientId = "esp8266-" + WiFi.macAddress();

// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}

// 发布信息
void pubMQTTmsg(){
static int value; // 客户端发布信息用数字

// 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。
// 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,
String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress();
char publishTopic[topicString.length() + 1];
strcpy(publishTopic, topicString.c_str());

// 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。
String messageString = "Hello World " + String(value++);
char publishMsg[messageString.length() + 1];
strcpy(publishMsg, messageString.c_str());

// 实现ESP8266向主题发布信息
if(mqttClient.publish(publishTopic, publishMsg)){
Serial.println("Publish Topic:");Serial.println(publishTopic);
Serial.println("Publish message:");Serial.println(publishMsg);
} else {
Serial.println("Message Publish Failed.");
}
}

// ESP8266连接wifi
void connectWifi(){

WiFi.begin(ssid, password);

//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}
\n"},{"title":"SpringMVC(一)快速入门","description":"springMVC概述、组件","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"890c8bdb","date":"2021-09-01T11:31:26.000Z","_content":"\n\n\n\n\n\n\n## 概述\n\nSpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便\n\n- 与Spring兼容性好,无缝结合\n\n- 约定优于配置\n\n\n\n## MVC设计模式\n\n\n\nMVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。\n\n使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。\n\n\n\n{% note info flat %}\nController用于接收客户端请求,调用Model生成业务数据,传递给View\n{% endnote %}\n\n\n\nspringMVC架构\n\n![springMVC架构](https://gitee.com/ajream/images/raw/master/img/20210902234928_1-1601161F914292.png)\n\n\n\n## 核心组件\n\nDispatcherServlet:\n\nMVC框架是围绕`DispatcherServlet`设计的,它处理所有的HTTP请求和响应。 **Spring Web MVC DispatcherServlet**的请求处理工作流如下图所示:\n\n![img](http://www.yiibai.com/uploads/images/201701/18/451110124_66519.png)\n\n以下是对应于到`DispatcherServlet`的传入HTTP请求的事件顺序:\n\n1. 在接收到HTTP请求后,`DispatcherServlet`会查询`HandlerMapping`以调用相应的`Controller`。\n2. `Controller`接受请求并根据使用的`GET`或`POST`方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给`DispatcherServlet`\n3. `DispatcherServlet`将从`ViewResolver`获取请求的定义视图。\n4. 当视图完成,`DispatcherServlet`将模型数据传递到最终的视图,并在浏览器上呈现。\n\n所有上述组件,即: `HandlerMapping`,`Controller`和`ViewResolver`是`WebApplicationContext`的一部分,它是普通`ApplicationContext`的扩展,带有Web应用程序所需的一些额外功能。\n\n\n\n{%folding blue, 配置%}\n\n\n\n需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。\n\n下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:\n\n\n\n```xml\n\n\n Spring MVC Application\n\n \n HelloWeb\n \n org.springframework.web.servlet.DispatcherServlet\n \n 1\n \n\n \n HelloWeb\n *.jsp\n \n\n\n```\n\n\n\n`web.xml`文件将保存Web应用程序的`WebContent/WEB-INF`目录。\n\n在`HelloWeb DispatcherServlet`初始化时,框架将尝试从位于应用程序的`WebContent/WEB-INF`目录中的名为`[servlet-name]-servlet.xml`的文件加载应用程序上下文。在这个示例中,使用的文件将是`HelloWeb-servlet.xml`。\n\n接下来,``标记指示哪些`URL`将由`DispatcherServlet`处理。 这里所有以`.jsp`结尾的HTTP请求都将由`HelloWeb DispatcherServlet`处理。\n\n\n\n如果不想使用默认文件名为`[servlet-name]-servlet.xml`和默认位置为`WebContent/WEB-INF`,可以通过在`web.xml`文件中添加`servlet`侦听器`ContextLoaderListener`来自定义此文件名和位置 如下\n\n\n\n```xml\n\n\n\n....\n\n contextConfigLocation\n /WEB-INF/HelloWeb-servlet.xml\n\n\n\n \n org.springframework.web.context.ContextLoaderListener\n \n\n\n```\n\n\n\n{%endfolding%}\n\n## 回顾创建一个Servlet程序\n\n[项目地址springMVC-01-Servlet](https://codechina.csdn.net/m0_46079750/springmvc)\n\n\n\n{% tabs 第一个springMVC程序%}\n\n\n\n\n\n```xml\n\n \n org.springframework\n spring-webmvc\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.13\n \n \n javax.servlet\n servlet-api\n 2.5\n \n \n javax.servlet.jsp\n jsp-api\n 2.2\n \n \n javax.servlet\n jstl\n 1.2\n \n\n```\n\n\n\n{%folding blue, note:普通maven项目转为web项目%}\n\n1. 右键该项目,选择 `Add Framework support `\n\n ![image-20210901210441382](https://gitee.com/ajream/images/raw/master/img/20210901210444_image-20210901210441382.png)\n\n2. 选择 `Web Application`-> `OK`\n\n ![image-20210901210605521](https://gitee.com/ajream/images/raw/master/img/20210901210606_image-20210901210605521.png)\n\n3. 结果生成 `web`文件夹,说明成功了\n\n ![image-20210901210837382](https://gitee.com/ajream/images/raw/master/img/20210901210838_image-20210901210837382.png)\n\n\n\n\n\n{%endfolding%}\n\n\n\n\n\n\n\n1. 实现HttpServlet接口\n\n ```java\n package com.ajream.sevlet;\n \n import javax.servlet.ServletException;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n public class HelloServlet extends HttpServlet {\n \n @Override\n protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n // 1. 获取前端参数\n String method = req.getParameter(\"method\");\n if(method.equals(\"add\")){\n req.getSession().setAttribute(\"msg\", \"执行了add方法\");\n }\n if(method.equals(\"delete\")){\n req.getSession().setAttribute(\"msg\", \"执行了delete方法\");\n }\n // 2. 调用业务层\n // 3. 视图转发或者重定向\n req.getRequestDispatcher(\"WEB-INF/jsp/test.jsp\").forward(req,resp);\n \n }\n @Override\n protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n doGet(req, resp);\n }\n }\n \n ```\n\n2. 新建视图文件 `web/WEB-INF/jsp/test.jsp`\n\n ```html\n \n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title\n \n \n ${msg}\n \n \n \n ```\n\n3. 配置 `web/WEB-INF/web.xml`\n\n ```xml\n \n \n \n hello\n com.ajream.sevlet.HelloServlet\n \n \n \n hello\n /hello\n \n \n \n \n \n \n \n index.jsp \n \n \n ```\n\n4. 新建表单 `web/WEB-INF/jsp/form.jsp`\n\n ```html\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title\n \n \n \n
\n \n \n
\n \n \n \n \n ```\n\n \n\n\n\n\n\n\n\n![image-20210902000157563](https://gitee.com/ajream/images/raw/master/img/20210902000200_image-20210902000157563.png)\n\n\n\n![image-20210902000426095](https://gitee.com/ajream/images/raw/master/img/20210902000427_image-20210902000426095.png)\n\n\n\n在 `Application context` 输入: `/`\n\n![image-20210902000536524](https://gitee.com/ajream/images/raw/master/img/20210902000537_image-20210902000536524.png)\n\n\n\n\n\n\n\n\n\n在浏览器输入:`http://localhost:8083/hello?method=add`,(端口号可能不同)\n\n结果如下:\n\n![image-20210902000703371](https://gitee.com/ajream/images/raw/master/img/20210902000704_image-20210902000703371.png)\n\n\n\n\n\n{%endtabs%}\n\n\n\n","source":"_posts/SpringMVC/springMVC(一)快速入门.md","raw":"---\ntitle: SpringMVC(一)快速入门\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: springMVC概述、组件\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: 890c8bdb\ndate: 2021-09-01 19:31:26\n---\n\n\n\n\n\n\n\n## 概述\n\nSpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便\n\n- 与Spring兼容性好,无缝结合\n\n- 约定优于配置\n\n\n\n## MVC设计模式\n\n\n\nMVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。\n\n使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。\n\n\n\n{% note info flat %}\nController用于接收客户端请求,调用Model生成业务数据,传递给View\n{% endnote %}\n\n\n\nspringMVC架构\n\n![springMVC架构](https://gitee.com/ajream/images/raw/master/img/20210902234928_1-1601161F914292.png)\n\n\n\n## 核心组件\n\nDispatcherServlet:\n\nMVC框架是围绕`DispatcherServlet`设计的,它处理所有的HTTP请求和响应。 **Spring Web MVC DispatcherServlet**的请求处理工作流如下图所示:\n\n![img](http://www.yiibai.com/uploads/images/201701/18/451110124_66519.png)\n\n以下是对应于到`DispatcherServlet`的传入HTTP请求的事件顺序:\n\n1. 在接收到HTTP请求后,`DispatcherServlet`会查询`HandlerMapping`以调用相应的`Controller`。\n2. `Controller`接受请求并根据使用的`GET`或`POST`方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给`DispatcherServlet`\n3. `DispatcherServlet`将从`ViewResolver`获取请求的定义视图。\n4. 当视图完成,`DispatcherServlet`将模型数据传递到最终的视图,并在浏览器上呈现。\n\n所有上述组件,即: `HandlerMapping`,`Controller`和`ViewResolver`是`WebApplicationContext`的一部分,它是普通`ApplicationContext`的扩展,带有Web应用程序所需的一些额外功能。\n\n\n\n{%folding blue, 配置%}\n\n\n\n需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。\n\n下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:\n\n\n\n```xml\n\n\n Spring MVC Application\n\n \n HelloWeb\n \n org.springframework.web.servlet.DispatcherServlet\n \n 1\n \n\n \n HelloWeb\n *.jsp\n \n\n\n```\n\n\n\n`web.xml`文件将保存Web应用程序的`WebContent/WEB-INF`目录。\n\n在`HelloWeb DispatcherServlet`初始化时,框架将尝试从位于应用程序的`WebContent/WEB-INF`目录中的名为`[servlet-name]-servlet.xml`的文件加载应用程序上下文。在这个示例中,使用的文件将是`HelloWeb-servlet.xml`。\n\n接下来,``标记指示哪些`URL`将由`DispatcherServlet`处理。 这里所有以`.jsp`结尾的HTTP请求都将由`HelloWeb DispatcherServlet`处理。\n\n\n\n如果不想使用默认文件名为`[servlet-name]-servlet.xml`和默认位置为`WebContent/WEB-INF`,可以通过在`web.xml`文件中添加`servlet`侦听器`ContextLoaderListener`来自定义此文件名和位置 如下\n\n\n\n```xml\n\n\n\n....\n\n contextConfigLocation\n /WEB-INF/HelloWeb-servlet.xml\n\n\n\n \n org.springframework.web.context.ContextLoaderListener\n \n\n\n```\n\n\n\n{%endfolding%}\n\n## 回顾创建一个Servlet程序\n\n[项目地址springMVC-01-Servlet](https://codechina.csdn.net/m0_46079750/springmvc)\n\n\n\n{% tabs 第一个springMVC程序%}\n\n\n\n\n\n```xml\n\n \n org.springframework\n spring-webmvc\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.13\n \n \n javax.servlet\n servlet-api\n 2.5\n \n \n javax.servlet.jsp\n jsp-api\n 2.2\n \n \n javax.servlet\n jstl\n 1.2\n \n\n```\n\n\n\n{%folding blue, note:普通maven项目转为web项目%}\n\n1. 右键该项目,选择 `Add Framework support `\n\n ![image-20210901210441382](https://gitee.com/ajream/images/raw/master/img/20210901210444_image-20210901210441382.png)\n\n2. 选择 `Web Application`-> `OK`\n\n ![image-20210901210605521](https://gitee.com/ajream/images/raw/master/img/20210901210606_image-20210901210605521.png)\n\n3. 结果生成 `web`文件夹,说明成功了\n\n ![image-20210901210837382](https://gitee.com/ajream/images/raw/master/img/20210901210838_image-20210901210837382.png)\n\n\n\n\n\n{%endfolding%}\n\n\n\n\n\n\n\n1. 实现HttpServlet接口\n\n ```java\n package com.ajream.sevlet;\n \n import javax.servlet.ServletException;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n public class HelloServlet extends HttpServlet {\n \n @Override\n protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n // 1. 获取前端参数\n String method = req.getParameter(\"method\");\n if(method.equals(\"add\")){\n req.getSession().setAttribute(\"msg\", \"执行了add方法\");\n }\n if(method.equals(\"delete\")){\n req.getSession().setAttribute(\"msg\", \"执行了delete方法\");\n }\n // 2. 调用业务层\n // 3. 视图转发或者重定向\n req.getRequestDispatcher(\"WEB-INF/jsp/test.jsp\").forward(req,resp);\n \n }\n @Override\n protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n doGet(req, resp);\n }\n }\n \n ```\n\n2. 新建视图文件 `web/WEB-INF/jsp/test.jsp`\n\n ```html\n \n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title\n \n \n ${msg}\n \n \n \n ```\n\n3. 配置 `web/WEB-INF/web.xml`\n\n ```xml\n \n \n \n hello\n com.ajream.sevlet.HelloServlet\n \n \n \n hello\n /hello\n \n \n \n \n \n \n \n index.jsp \n \n \n ```\n\n4. 新建表单 `web/WEB-INF/jsp/form.jsp`\n\n ```html\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title\n \n \n \n
\n \n \n
\n \n \n \n \n ```\n\n \n\n\n\n\n\n\n\n![image-20210902000157563](https://gitee.com/ajream/images/raw/master/img/20210902000200_image-20210902000157563.png)\n\n\n\n![image-20210902000426095](https://gitee.com/ajream/images/raw/master/img/20210902000427_image-20210902000426095.png)\n\n\n\n在 `Application context` 输入: `/`\n\n![image-20210902000536524](https://gitee.com/ajream/images/raw/master/img/20210902000537_image-20210902000536524.png)\n\n\n\n\n\n\n\n\n\n在浏览器输入:`http://localhost:8083/hello?method=add`,(端口号可能不同)\n\n结果如下:\n\n![image-20210902000703371](https://gitee.com/ajream/images/raw/master/img/20210902000704_image-20210902000703371.png)\n\n\n\n\n\n{%endtabs%}\n\n\n\n","slug":"SpringMVC/springMVC(一)快速入门","published":1,"updated":"2021-09-05T10:33:47.417Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q4004makve8gio9l5r","content":"

概述

SpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便

\n
    \n
  • 与Spring兼容性好,无缝结合

    \n
  • \n
  • 约定优于配置

    \n
  • \n
\n

MVC设计模式

MVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。

\n

使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。

\n

Controller用于接收客户端请求,调用Model生成业务数据,传递给View

\n
\n

springMVC架构

\n

\"springMVC架构\"

\n

核心组件

DispatcherServlet:

\n

MVC框架是围绕DispatcherServlet设计的,它处理所有的HTTP请求和响应。 Spring Web MVC DispatcherServlet的请求处理工作流如下图所示:

\n

\"img\"

\n

以下是对应于到DispatcherServlet的传入HTTP请求的事件顺序:

\n
    \n
  1. 在接收到HTTP请求后,DispatcherServlet会查询HandlerMapping以调用相应的Controller
  2. \n
  3. Controller接受请求并根据使用的GETPOST方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给DispatcherServlet
  4. \n
  5. DispatcherServlet将从ViewResolver获取请求的定义视图。
  6. \n
  7. 当视图完成,DispatcherServlet将模型数据传递到最终的视图,并在浏览器上呈现。
  8. \n
\n

所有上述组件,即: HandlerMappingControllerViewResolverWebApplicationContext的一部分,它是普通ApplicationContext的扩展,带有Web应用程序所需的一些额外功能。

\n
配置 \n
\n

需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。

下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<display-name>Spring MVC Application</display-name>

<servlet>
<servlet-name>HelloWeb</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>HelloWeb</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>

</web-app>

web.xml文件将保存Web应用程序的WebContent/WEB-INF目录。

HelloWeb DispatcherServlet初始化时,框架将尝试从位于应用程序的WebContent/WEB-INF目录中的名为[servlet-name]-servlet.xml的文件加载应用程序上下文。在这个示例中,使用的文件将是HelloWeb-servlet.xml

接下来,<servlet-mapping>标记指示哪些URL将由DispatcherServlet处理。 这里所有以.jsp结尾的HTTP请求都将由HelloWeb DispatcherServlet处理。

如果不想使用默认文件名为[servlet-name]-servlet.xml和默认位置为WebContent/WEB-INF,可以通过在web.xml文件中添加servlet侦听器ContextLoaderListener来自定义此文件名和位置 如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<web-app...>

<!-------- DispatcherServlet definition goes here----->
....
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/HelloWeb-servlet.xml</param-value>
</context-param>

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
\n
\n
\n

回顾创建一个Servlet程序

项目地址springMVC-01-Servlet

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
\n
note:普通maven项目转为web项目 \n
\n
  1. 右键该项目,选择 Add Framework support

    \"image-20210901210441382\"

  2. 选择 Web Application-> OK

    \"image-20210901210605521\"

  3. 结果生成 web文件夹,说明成功了

    \"image-20210901210837382\"

\n
\n
    \n
  1. 实现HttpServlet接口

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package com.ajream.sevlet;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 1. 获取前端参数
    String method = req.getParameter("method");
    if(method.equals("add")){
    req.getSession().setAttribute("msg", "执行了add方法");
    }
    if(method.equals("delete")){
    req.getSession().setAttribute("msg", "执行了delete方法");
    }
    // 2. 调用业务层
    // 3. 视图转发或者重定向
    req.getRequestDispatcher("WEB-INF/jsp/test.jsp").forward(req,resp);

    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doGet(req, resp);
    }
    }

    \n
  2. \n
  3. 新建视图文件 web/WEB-INF/jsp/test.jsp

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>

    \n
  4. \n
  5. 配置 web/WEB-INF/web.xml

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.ajream.sevlet.HelloServlet</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <!-- <session-config>-->
    <!-- <session-timeout>15</session-timeout> &lt;!&ndash;15分钟超时&ndash;&gt;-->
    <!-- </session-config>-->

    <welcome-file-list>
    <welcome-file>index.jsp</welcome-file> <!--配置欢迎页面-->
    </welcome-file-list>
    </web-app>
    \n
  6. \n
  7. 新建表单 web/WEB-INF/jsp/form.jsp

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>

    <form action="/hello" method="post">
    <input type="text" name="method">
    <input type="submit">
    </form>

    </body>
    </html>

    \n
  8. \n

\"image-20210902000157563\"

\n

\"image-20210902000426095\"

\n

Application context 输入: /

\n

\"image-20210902000536524\"

在浏览器输入:http://localhost:8083/hello?method=add,(端口号可能不同)

\n

结果如下:

\n

\"image-20210902000703371\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

概述

SpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便

\n
    \n
  • 与Spring兼容性好,无缝结合

    \n
  • \n
  • 约定优于配置

    \n
  • \n
\n

MVC设计模式

MVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。

\n

使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。

\n

Controller用于接收客户端请求,调用Model生成业务数据,传递给View

\n
\n

springMVC架构

\n

\"springMVC架构\"

\n

核心组件

DispatcherServlet:

\n

MVC框架是围绕DispatcherServlet设计的,它处理所有的HTTP请求和响应。 Spring Web MVC DispatcherServlet的请求处理工作流如下图所示:

\n

\"img\"

\n

以下是对应于到DispatcherServlet的传入HTTP请求的事件顺序:

\n
    \n
  1. 在接收到HTTP请求后,DispatcherServlet会查询HandlerMapping以调用相应的Controller
  2. \n
  3. Controller接受请求并根据使用的GETPOST方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给DispatcherServlet
  4. \n
  5. DispatcherServlet将从ViewResolver获取请求的定义视图。
  6. \n
  7. 当视图完成,DispatcherServlet将模型数据传递到最终的视图,并在浏览器上呈现。
  8. \n
\n

所有上述组件,即: HandlerMappingControllerViewResolverWebApplicationContext的一部分,它是普通ApplicationContext的扩展,带有Web应用程序所需的一些额外功能。

\n
配置 \n
\n

需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。

下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<display-name>Spring MVC Application</display-name>

<servlet>
<servlet-name>HelloWeb</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>HelloWeb</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>

</web-app>

web.xml文件将保存Web应用程序的WebContent/WEB-INF目录。

HelloWeb DispatcherServlet初始化时,框架将尝试从位于应用程序的WebContent/WEB-INF目录中的名为[servlet-name]-servlet.xml的文件加载应用程序上下文。在这个示例中,使用的文件将是HelloWeb-servlet.xml

接下来,<servlet-mapping>标记指示哪些URL将由DispatcherServlet处理。 这里所有以.jsp结尾的HTTP请求都将由HelloWeb DispatcherServlet处理。

如果不想使用默认文件名为[servlet-name]-servlet.xml和默认位置为WebContent/WEB-INF,可以通过在web.xml文件中添加servlet侦听器ContextLoaderListener来自定义此文件名和位置 如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<web-app...>

<!-------- DispatcherServlet definition goes here----->
....
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/HelloWeb-servlet.xml</param-value>
</context-param>

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
\n
\n
\n

回顾创建一个Servlet程序

项目地址springMVC-01-Servlet

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
\n
note:普通maven项目转为web项目 \n
\n
  1. 右键该项目,选择 Add Framework support

    \"image-20210901210441382\"

  2. 选择 Web Application-> OK

    \"image-20210901210605521\"

  3. 结果生成 web文件夹,说明成功了

    \"image-20210901210837382\"

\n
\n
    \n
  1. 实现HttpServlet接口

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package com.ajream.sevlet;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 1. 获取前端参数
    String method = req.getParameter("method");
    if(method.equals("add")){
    req.getSession().setAttribute("msg", "执行了add方法");
    }
    if(method.equals("delete")){
    req.getSession().setAttribute("msg", "执行了delete方法");
    }
    // 2. 调用业务层
    // 3. 视图转发或者重定向
    req.getRequestDispatcher("WEB-INF/jsp/test.jsp").forward(req,resp);

    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doGet(req, resp);
    }
    }

    \n
  2. \n
  3. 新建视图文件 web/WEB-INF/jsp/test.jsp

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>

    \n
  4. \n
  5. 配置 web/WEB-INF/web.xml

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.ajream.sevlet.HelloServlet</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <!-- <session-config>-->
    <!-- <session-timeout>15</session-timeout> &lt;!&ndash;15分钟超时&ndash;&gt;-->
    <!-- </session-config>-->

    <welcome-file-list>
    <welcome-file>index.jsp</welcome-file> <!--配置欢迎页面-->
    </welcome-file-list>
    </web-app>
    \n
  6. \n
  7. 新建表单 web/WEB-INF/jsp/form.jsp

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>

    <form action="/hello" method="post">
    <input type="text" name="method">
    <input type="submit">
    </form>

    </body>
    </html>

    \n
  8. \n

\"image-20210902000157563\"

\n

\"image-20210902000426095\"

\n

Application context 输入: /

\n

\"image-20210902000536524\"

在浏览器输入:http://localhost:8083/hello?method=add,(端口号可能不同)

\n

结果如下:

\n

\"image-20210902000703371\"

\n"},{"title":"SpringMVC(三)HelloMVC(注解版)","description":"SpringMVC(注解版本)的HelloWorld程序","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"b8426075","date":"2021-09-03T07:01:20.000Z","_content":"\n\n\n\n\n\n\n[项目地址springMVC-03-hello-annotetion](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 新建一个project:`springmvc-03-hello-annotation`, 添加依赖,添加web支持!\n\n2. 建立包结构 com.ajream.controller\n\n3. 由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:\n\n ```xml\n \n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n false\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n false\n \n \n \n ```\n\n\n\n## 进行开发\n\n### 配置web.xml\n\n注意点:\n\n- 注意web.xml版本问题,要最新版[4.0版]!\n- 注册DispatcherServlet\n- 关联SpringMVC的配置文件:`springmvc-servlet.xml`(先在resources新建springmvc-servlet.xml文件)\n- 启动级别为1\n- 映射路径为 `/` 【不要用`/*`,会404】\n\n\n\n```xml\n\n\n\n \n \n SpringMVC\n org.springframework.web.servlet.DispatcherServlet\n \n \n \n contextConfigLocation\n \n classpath:springmvc-servlet.xml \n \n \n \n 1\n \n \n \n \n SpringMVC\n /\n \n\n\n```\n\n\n\n{%note info flat%}\n\n注意`/` 和 `/*` 的区别:\n\n` / ` 不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类\n\n` /* ` 会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错\n\n{%endnote%}\n\n\n\n\n\n### 添加Spring MVC配置文件\n\n- 让IOC的注解生效\n- 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..\n- MVC的注解驱动\n- 配置视图解析器\n\n在resource目录下添加`springmvc-servlet.xml`配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:\n\n\n\n```xml\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n{%note info flat%}\n\n在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。\n\n{%endnote%}\n\n\n\n### 创建Controller\n\n\n\n编写一个Java控制类: `com.kuang.controller.HelloController` , 注意编码规范\n\n- `@Controller`是为了让Spring IOC容器初始化时自动扫描到;\n- `@RequestMapping`是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;\n- 方法中声明Model类型的参数是为了把Action中的数据带到视图中;\n- 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/**hello**.jsp。\n\n```java\npackage com.kuang.controller;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/HelloController\")\npublic class HelloController {\n \n //真实访问地址 : 项目名/HelloController/hello\n @RequestMapping(\"/hello\")\n public String sayHello(Model model){\n \n //向模型中添加属性msg与值,可以在JSP页面中取出并渲染\n model.addAttribute(\"msg\",\"hello,SpringMVC\");\n \n //web-inf/jsp/hello.jsp\n return \"hello\";\n }\n}\n```\n\n\n\n\n\n### 创建视图层\n\n在WEB-INF/ jsp目录中创建`hello.jsp` , 视图可以直接取出并展示从Controller带回的信息;\n\n可以通过EL表示取出Model中存放的值,或者对象;\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n SpringMVC\n\n\n ${msg}\n\n\n```\n\n\n\n\n\n### 配置Tomcat运行\n\n配置Tomcat , 开启服务器 , 访问对应的请求路径 `http://localhost:8083/HelloController/hello`\n\n![image-20210903225624996](https://gitee.com/ajream/images/raw/master/img/20210903225626_image-20210903225624996.png)\n\n","source":"_posts/SpringMVC/springMVC(三)使用注解开发.md","raw":"---\ntitle: SpringMVC(三)HelloMVC(注解版)\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: SpringMVC(注解版本)的HelloWorld程序\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: b8426075\ndate: 2021-09-03 15:01:20\n---\n\n\n\n\n\n\n\n[项目地址springMVC-03-hello-annotetion](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 新建一个project:`springmvc-03-hello-annotation`, 添加依赖,添加web支持!\n\n2. 建立包结构 com.ajream.controller\n\n3. 由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:\n\n ```xml\n \n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n false\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n false\n \n \n \n ```\n\n\n\n## 进行开发\n\n### 配置web.xml\n\n注意点:\n\n- 注意web.xml版本问题,要最新版[4.0版]!\n- 注册DispatcherServlet\n- 关联SpringMVC的配置文件:`springmvc-servlet.xml`(先在resources新建springmvc-servlet.xml文件)\n- 启动级别为1\n- 映射路径为 `/` 【不要用`/*`,会404】\n\n\n\n```xml\n\n\n\n \n \n SpringMVC\n org.springframework.web.servlet.DispatcherServlet\n \n \n \n contextConfigLocation\n \n classpath:springmvc-servlet.xml \n \n \n \n 1\n \n \n \n \n SpringMVC\n /\n \n\n\n```\n\n\n\n{%note info flat%}\n\n注意`/` 和 `/*` 的区别:\n\n` / ` 不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类\n\n` /* ` 会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错\n\n{%endnote%}\n\n\n\n\n\n### 添加Spring MVC配置文件\n\n- 让IOC的注解生效\n- 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..\n- MVC的注解驱动\n- 配置视图解析器\n\n在resource目录下添加`springmvc-servlet.xml`配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:\n\n\n\n```xml\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n{%note info flat%}\n\n在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。\n\n{%endnote%}\n\n\n\n### 创建Controller\n\n\n\n编写一个Java控制类: `com.kuang.controller.HelloController` , 注意编码规范\n\n- `@Controller`是为了让Spring IOC容器初始化时自动扫描到;\n- `@RequestMapping`是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;\n- 方法中声明Model类型的参数是为了把Action中的数据带到视图中;\n- 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/**hello**.jsp。\n\n```java\npackage com.kuang.controller;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/HelloController\")\npublic class HelloController {\n \n //真实访问地址 : 项目名/HelloController/hello\n @RequestMapping(\"/hello\")\n public String sayHello(Model model){\n \n //向模型中添加属性msg与值,可以在JSP页面中取出并渲染\n model.addAttribute(\"msg\",\"hello,SpringMVC\");\n \n //web-inf/jsp/hello.jsp\n return \"hello\";\n }\n}\n```\n\n\n\n\n\n### 创建视图层\n\n在WEB-INF/ jsp目录中创建`hello.jsp` , 视图可以直接取出并展示从Controller带回的信息;\n\n可以通过EL表示取出Model中存放的值,或者对象;\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n SpringMVC\n\n\n ${msg}\n\n\n```\n\n\n\n\n\n### 配置Tomcat运行\n\n配置Tomcat , 开启服务器 , 访问对应的请求路径 `http://localhost:8083/HelloController/hello`\n\n![image-20210903225624996](https://gitee.com/ajream/images/raw/master/img/20210903225626_image-20210903225624996.png)\n\n","slug":"SpringMVC/springMVC(三)使用注解开发","published":1,"updated":"2021-09-05T10:34:03.284Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q5004oakve8g0k1g9x","content":"

项目地址springMVC-03-hello-annotetion

\n

环境配置

    \n
  1. 新建一个project:springmvc-03-hello-annotation, 添加依赖,添加web支持!

    \n
  2. \n
  3. 建立包结构 com.ajream.controller

    \n
  4. \n
  5. 由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <build>
    <resources>
    <resource>
    <directory>src/main/java</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>false</filtering>
    </resource>
    <resource>
    <directory>src/main/resources</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>false</filtering>
    </resource>
    </resources>
    </build>
    \n
  6. \n
\n

进行开发

配置web.xml

注意点:

\n
    \n
  • 注意web.xml版本问题,要最新版[4.0版]!
  • \n
  • 注册DispatcherServlet
  • \n
  • 关联SpringMVC的配置文件:springmvc-servlet.xml(先在resources新建springmvc-servlet.xml文件)
  • \n
  • 启动级别为1
  • \n
  • 映射路径为 / 【不要用/*,会404】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--1.注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--在resources新建springmvc-servlet.xml文件-->
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
\n

注意//* 的区别:

\n

<url-pattern> / </url-pattern> 不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类

\n

<url-pattern> /* </url-pattern> 会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错

\n
\n

添加Spring MVC配置文件

    \n
  • 让IOC的注解生效
  • \n
  • 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..
  • \n
  • MVC的注解驱动
  • \n
  • 配置视图解析器
  • \n
\n

在resource目录下添加springmvc-servlet.xml配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.kuang.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
\n

在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。

\n
\n

创建Controller

编写一个Java控制类: com.kuang.controller.HelloController , 注意编码规范

\n
    \n
  • @Controller是为了让Spring IOC容器初始化时自动扫描到;
  • \n
  • @RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;
  • \n
  • 方法中声明Model类型的参数是为了把Action中的数据带到视图中;
  • \n
  • 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp。
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.kuang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/HelloController")
public class HelloController {

//真实访问地址 : 项目名/HelloController/hello
@RequestMapping("/hello")
public String sayHello(Model model){

//向模型中添加属性msg与值,可以在JSP页面中取出并渲染
model.addAttribute("msg","hello,SpringMVC");

//web-inf/jsp/hello.jsp
return "hello";
}
}
\n

创建视图层

在WEB-INF/ jsp目录中创建hello.jsp , 视图可以直接取出并展示从Controller带回的信息;

\n

可以通过EL表示取出Model中存放的值,或者对象;

\n
1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SpringMVC</title>
</head>
<body>
${msg}
</body>
</html>
\n

配置Tomcat运行

配置Tomcat , 开启服务器 , 访问对应的请求路径 http://localhost:8083/HelloController/hello

\n

\"image-20210903225624996\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

项目地址springMVC-03-hello-annotetion

\n

环境配置

    \n
  1. 新建一个project:springmvc-03-hello-annotation, 添加依赖,添加web支持!

    \n
  2. \n
  3. 建立包结构 com.ajream.controller

    \n
  4. \n
  5. 由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <build>
    <resources>
    <resource>
    <directory>src/main/java</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>false</filtering>
    </resource>
    <resource>
    <directory>src/main/resources</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>false</filtering>
    </resource>
    </resources>
    </build>
    \n
  6. \n
\n

进行开发

配置web.xml

注意点:

\n
    \n
  • 注意web.xml版本问题,要最新版[4.0版]!
  • \n
  • 注册DispatcherServlet
  • \n
  • 关联SpringMVC的配置文件:springmvc-servlet.xml(先在resources新建springmvc-servlet.xml文件)
  • \n
  • 启动级别为1
  • \n
  • 映射路径为 / 【不要用/*,会404】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--1.注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--在resources新建springmvc-servlet.xml文件-->
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
\n

注意//* 的区别:

\n

<url-pattern> / </url-pattern> 不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类

\n

<url-pattern> /* </url-pattern> 会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错

\n
\n

添加Spring MVC配置文件

    \n
  • 让IOC的注解生效
  • \n
  • 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..
  • \n
  • MVC的注解驱动
  • \n
  • 配置视图解析器
  • \n
\n

在resource目录下添加springmvc-servlet.xml配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.kuang.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
\n

在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。

\n
\n

创建Controller

编写一个Java控制类: com.kuang.controller.HelloController , 注意编码规范

\n
    \n
  • @Controller是为了让Spring IOC容器初始化时自动扫描到;
  • \n
  • @RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;
  • \n
  • 方法中声明Model类型的参数是为了把Action中的数据带到视图中;
  • \n
  • 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp。
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.kuang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/HelloController")
public class HelloController {

//真实访问地址 : 项目名/HelloController/hello
@RequestMapping("/hello")
public String sayHello(Model model){

//向模型中添加属性msg与值,可以在JSP页面中取出并渲染
model.addAttribute("msg","hello,SpringMVC");

//web-inf/jsp/hello.jsp
return "hello";
}
}
\n

创建视图层

在WEB-INF/ jsp目录中创建hello.jsp , 视图可以直接取出并展示从Controller带回的信息;

\n

可以通过EL表示取出Model中存放的值,或者对象;

\n
1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SpringMVC</title>
</head>
<body>
${msg}
</body>
</html>
\n

配置Tomcat运行

配置Tomcat , 开启服务器 , 访问对应的请求路径 http://localhost:8083/HelloController/hello

\n

\"image-20210903225624996\"

\n"},{"title":"SpringMVC(六)数据处理和乱码问题","description":"对提交的数据进行处理以及乱码问题的解决","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"ecec7d3f","date":"2021-09-04T08:26:20.000Z","_content":"\n\n\n## 获取url提交的数据\n\n\n\n[项目地址springMVC-06-dataProcess](https://codechina.csdn.net/m0_46079750/springmvc/-/tree/master1)\n\n### 通过url提交的数据\n\n**url的参数名与java方法中的参数名一致**(方法名没要求),如下:都是 `name`\n\nurl: http://localhost:8083/hello?name=ajream\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**url的参数名与java方法中的参数名不一致**, 如下,url中为 `username`, java方法中为 `name`\n\nurl: http://localhost:8083/hello?username=ajream\n\n使用注解:`@RequestParam`\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(@RequestParam(\"username\") String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**传入的数据封装成对象**\n\n首先创建一个 `User` 类\n\n```java\npackage com.ajream.pojo;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class User {\n private String name;\n private int id;\n private int age;\n}\n\n```\n\n在 `Controller`中,添加一个参数 `User user`,表示传入的参数将会被封装成一个 `user` 对象\n\n```java\n@RequestMapping(\"/t2\")\npublic String test2(User user, Model model){\n System.out.println(user);\n model.addAttribute(\"msg\", user);\n return \"hello\";\n}\n```\n\n\n\n测试运行,访问 `http://localhost:8083/t2?name=ajream&age=10&id=10001` :\n\n![image-20210904190448500](https://gitee.com/ajream/images/raw/master/img/20210904190452_image-20210904190448500.png)\n\n\n\n> 注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下\n>\n> ![image-20210904190750826](https://gitee.com/ajream/images/raw/master/img/20210904190752_image-20210904190750826.png)\n\n\n\n\n\n\n\n## 表单提交的数据乱码\n\n### POST提交数据乱码\n\n创建表单提交页面 `web/form.jsp`\n\n![image-20210904195836766](https://gitee.com/ajream/images/raw/master/img/20210904195839_image-20210904195836766.png)\n\n添加代码:\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title\n\n\n
\n \n \n
\n\n\n\n\n```\n\n\n\n编写后台处理类\n\n```java\npackage com.ajream.encoding;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.PostMapping;\n\n@Controller\npublic class EncodingController {\n @PostMapping(\"/e/t1\")\n public String test1(String name, Model model){\n model.addAttribute(\"msg\", name);\n System.out.println(name);\n return \"test\";\n }\n}\n\n```\n\n运行测试,先访问表单页 `http:localhost:8083/form.jsp`, 提交后自动跳转到 `localhost:8083/e/t1`页面\n\n传入中文数据,返回乱码:\n\n![image-20210904200350867](https://gitee.com/ajream/images/raw/master/img/20210904200352_image-20210904200350867.png)\n\n后台也是:\n\n![image-20210904200522875](https://gitee.com/ajream/images/raw/master/img/20210904200524_image-20210904200522875.png)\n\n\n\n\n\n{%folding blue, 提交表单后出现bug%}\n\n提交表单出现404页面\n\n![image-20210904200902942](https://gitee.com/ajream/images/raw/master/img/20210904200904_image-20210904200902942.png)\n\n\n\n原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决\n\n![image-20210904201311953](https://gitee.com/ajream/images/raw/master/img/20210904201313_image-20210904201311953.png)\n\n\n\n{%endfolding%}\n\n\n\n\n\n### 乱码问题解决\n\n\n\n在 `web.xml` 添加过滤器(这里用的是springMVC提供的)\n\n\n\n```xml\n\n encoding\n org.springframework.web.filter.CharacterEncodingFilter\n \n encoding\n utf-8\n \n\n\n encoding\n /* \n\n```\n\n{%note info flat%}\n\n注意`url-pattern`是 \"/*\", 不是 \"/\"\n\n{%endnote%}\n\n\n\n重新运行,前端、后台都显示正常:\n\n| 前端 | 后台 |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210904202053181](https://gitee.com/ajream/images/raw/master/img/20210904202054_image-20210904202053181.png) | \"image-20210904202144232\" |\n\n\n\n\n\n{%folding blue, 极端情况对get请求支持不好%}\n\n有些极端情况下.这个过滤器对get的支持不好 .\n\n处理方法 :\n\n修改tomcat配置文件`server.xml`(设置编码)\n\n```xml\n\n```\n\n自定义过滤器:\n\n```java\npackage com.kuang.filter;\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Map;\n/**\n * 解决get和post请求 全部乱码的过滤器\n */\npublic class GenericEncodingFilter implements Filter {\n @Override\n public void destroy() {\n }\n @Override\n public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\n //处理response的字符编码\n HttpServletResponse myResponse=(HttpServletResponse) response;\n myResponse.setContentType(\"text/html;charset=UTF-8\");\n // 转型为与协议相关对象\n HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n // 对request包装增强\n HttpServletRequest myrequest = new MyRequest(httpServletRequest);\n chain.doFilter(myrequest, response);\n }\n @Override\n public void init(FilterConfig filterConfig) throws ServletException {\n }\n}\n//自定义request对象,HttpServletRequest的包装类\nclass MyRequest extends HttpServletRequestWrapper {\n private HttpServletRequest request;\n //是否编码的标记\n private boolean hasEncode;\n //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰\n public MyRequest(HttpServletRequest request) {\n super(request);// super必须写\n this.request = request;\n }\n // 对需要增强方法 进行覆盖\n @Override\n public Map getParameterMap() {\n // 先获得请求方式\n String method = request.getMethod();\n if (method.equalsIgnoreCase(\"post\")) {\n // post请求\n try {\n // 处理post乱码\n request.setCharacterEncoding(\"utf-8\");\n return request.getParameterMap();\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n } else if (method.equalsIgnoreCase(\"get\")) {\n // get请求\n Map parameterMap = request.getParameterMap();\n if (!hasEncode) { // 确保get手动编码逻辑只运行一次\n for (String parameterName : parameterMap.keySet()) {\n String[] values = parameterMap.get(parameterName);\n if (values != null) {\n for (int i = 0; i < values.length; i++) {\n try {\n // 处理get乱码\n values[i] = new String(values[i]\n .getBytes(\"ISO-8859-1\"), \"utf-8\");\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n }\n }\n }\n hasEncode = true;\n }\n return parameterMap;\n }\n return super.getParameterMap();\n }\n //取一个值\n @Override\n public String getParameter(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n if (values == null) {\n return null;\n }\n return values[0]; // 取回参数的第一个值\n }\n //取所有值\n @Override\n public String[] getParameterValues(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n return values;\n }\n}\n```\n\n\n\n在web.xml添加自定义过滤器即可\n\n```xml\n\n encoding\n 【修改为自定义过滤器】\n \n encoding\n utf-8\n \n\n\n encoding\n /* \n\n```\n\n\n\n{%note flat%}\n\n乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n\n\n","source":"_posts/SpringMVC/springMVC(六)数据处理.md","raw":"---\ntitle: SpringMVC(六)数据处理和乱码问题\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: 对提交的数据进行处理以及乱码问题的解决\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: ecec7d3f\ndate: 2021-09-04 16:26:20\n---\n\n\n\n## 获取url提交的数据\n\n\n\n[项目地址springMVC-06-dataProcess](https://codechina.csdn.net/m0_46079750/springmvc/-/tree/master1)\n\n### 通过url提交的数据\n\n**url的参数名与java方法中的参数名一致**(方法名没要求),如下:都是 `name`\n\nurl: http://localhost:8083/hello?name=ajream\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**url的参数名与java方法中的参数名不一致**, 如下,url中为 `username`, java方法中为 `name`\n\nurl: http://localhost:8083/hello?username=ajream\n\n使用注解:`@RequestParam`\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(@RequestParam(\"username\") String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**传入的数据封装成对象**\n\n首先创建一个 `User` 类\n\n```java\npackage com.ajream.pojo;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class User {\n private String name;\n private int id;\n private int age;\n}\n\n```\n\n在 `Controller`中,添加一个参数 `User user`,表示传入的参数将会被封装成一个 `user` 对象\n\n```java\n@RequestMapping(\"/t2\")\npublic String test2(User user, Model model){\n System.out.println(user);\n model.addAttribute(\"msg\", user);\n return \"hello\";\n}\n```\n\n\n\n测试运行,访问 `http://localhost:8083/t2?name=ajream&age=10&id=10001` :\n\n![image-20210904190448500](https://gitee.com/ajream/images/raw/master/img/20210904190452_image-20210904190448500.png)\n\n\n\n> 注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下\n>\n> ![image-20210904190750826](https://gitee.com/ajream/images/raw/master/img/20210904190752_image-20210904190750826.png)\n\n\n\n\n\n\n\n## 表单提交的数据乱码\n\n### POST提交数据乱码\n\n创建表单提交页面 `web/form.jsp`\n\n![image-20210904195836766](https://gitee.com/ajream/images/raw/master/img/20210904195839_image-20210904195836766.png)\n\n添加代码:\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title\n\n\n
\n \n \n
\n\n\n\n\n```\n\n\n\n编写后台处理类\n\n```java\npackage com.ajream.encoding;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.PostMapping;\n\n@Controller\npublic class EncodingController {\n @PostMapping(\"/e/t1\")\n public String test1(String name, Model model){\n model.addAttribute(\"msg\", name);\n System.out.println(name);\n return \"test\";\n }\n}\n\n```\n\n运行测试,先访问表单页 `http:localhost:8083/form.jsp`, 提交后自动跳转到 `localhost:8083/e/t1`页面\n\n传入中文数据,返回乱码:\n\n![image-20210904200350867](https://gitee.com/ajream/images/raw/master/img/20210904200352_image-20210904200350867.png)\n\n后台也是:\n\n![image-20210904200522875](https://gitee.com/ajream/images/raw/master/img/20210904200524_image-20210904200522875.png)\n\n\n\n\n\n{%folding blue, 提交表单后出现bug%}\n\n提交表单出现404页面\n\n![image-20210904200902942](https://gitee.com/ajream/images/raw/master/img/20210904200904_image-20210904200902942.png)\n\n\n\n原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决\n\n![image-20210904201311953](https://gitee.com/ajream/images/raw/master/img/20210904201313_image-20210904201311953.png)\n\n\n\n{%endfolding%}\n\n\n\n\n\n### 乱码问题解决\n\n\n\n在 `web.xml` 添加过滤器(这里用的是springMVC提供的)\n\n\n\n```xml\n\n encoding\n org.springframework.web.filter.CharacterEncodingFilter\n \n encoding\n utf-8\n \n\n\n encoding\n /* \n\n```\n\n{%note info flat%}\n\n注意`url-pattern`是 \"/*\", 不是 \"/\"\n\n{%endnote%}\n\n\n\n重新运行,前端、后台都显示正常:\n\n| 前端 | 后台 |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210904202053181](https://gitee.com/ajream/images/raw/master/img/20210904202054_image-20210904202053181.png) | \"image-20210904202144232\" |\n\n\n\n\n\n{%folding blue, 极端情况对get请求支持不好%}\n\n有些极端情况下.这个过滤器对get的支持不好 .\n\n处理方法 :\n\n修改tomcat配置文件`server.xml`(设置编码)\n\n```xml\n\n```\n\n自定义过滤器:\n\n```java\npackage com.kuang.filter;\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Map;\n/**\n * 解决get和post请求 全部乱码的过滤器\n */\npublic class GenericEncodingFilter implements Filter {\n @Override\n public void destroy() {\n }\n @Override\n public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\n //处理response的字符编码\n HttpServletResponse myResponse=(HttpServletResponse) response;\n myResponse.setContentType(\"text/html;charset=UTF-8\");\n // 转型为与协议相关对象\n HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n // 对request包装增强\n HttpServletRequest myrequest = new MyRequest(httpServletRequest);\n chain.doFilter(myrequest, response);\n }\n @Override\n public void init(FilterConfig filterConfig) throws ServletException {\n }\n}\n//自定义request对象,HttpServletRequest的包装类\nclass MyRequest extends HttpServletRequestWrapper {\n private HttpServletRequest request;\n //是否编码的标记\n private boolean hasEncode;\n //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰\n public MyRequest(HttpServletRequest request) {\n super(request);// super必须写\n this.request = request;\n }\n // 对需要增强方法 进行覆盖\n @Override\n public Map getParameterMap() {\n // 先获得请求方式\n String method = request.getMethod();\n if (method.equalsIgnoreCase(\"post\")) {\n // post请求\n try {\n // 处理post乱码\n request.setCharacterEncoding(\"utf-8\");\n return request.getParameterMap();\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n } else if (method.equalsIgnoreCase(\"get\")) {\n // get请求\n Map parameterMap = request.getParameterMap();\n if (!hasEncode) { // 确保get手动编码逻辑只运行一次\n for (String parameterName : parameterMap.keySet()) {\n String[] values = parameterMap.get(parameterName);\n if (values != null) {\n for (int i = 0; i < values.length; i++) {\n try {\n // 处理get乱码\n values[i] = new String(values[i]\n .getBytes(\"ISO-8859-1\"), \"utf-8\");\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n }\n }\n }\n hasEncode = true;\n }\n return parameterMap;\n }\n return super.getParameterMap();\n }\n //取一个值\n @Override\n public String getParameter(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n if (values == null) {\n return null;\n }\n return values[0]; // 取回参数的第一个值\n }\n //取所有值\n @Override\n public String[] getParameterValues(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n return values;\n }\n}\n```\n\n\n\n在web.xml添加自定义过滤器即可\n\n```xml\n\n encoding\n 【修改为自定义过滤器】\n \n encoding\n utf-8\n \n\n\n encoding\n /* \n\n```\n\n\n\n{%note flat%}\n\n乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n\n\n","slug":"SpringMVC/springMVC(六)数据处理","published":1,"updated":"2021-09-05T10:34:16.597Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q6004rakve7m1j1plt","content":"

获取url提交的数据

项目地址springMVC-06-dataProcess

\n

通过url提交的数据

url的参数名与java方法中的参数名一致(方法名没要求),如下:都是 name

\n

url: http://localhost:8083/hello?name=ajream

\n
1
2
3
4
5
@RequestMapping("/hello")
public String hello(String name){
System.out.println(name);
return "hello";
}
\n

url的参数名与java方法中的参数名不一致, 如下,url中为 username, java方法中为 name

\n

url: http://localhost:8083/hello?username=ajream

\n

使用注解:@RequestParam

\n
1
2
3
4
5
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
System.out.println(name);
return "hello";
}
\n

传入的数据封装成对象

\n

首先创建一个 User

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int id;
private int age;
}

\n

Controller中,添加一个参数 User user,表示传入的参数将会被封装成一个 user 对象

\n
1
2
3
4
5
6
@RequestMapping("/t2")
public String test2(User user, Model model){
System.out.println(user);
model.addAttribute("msg", user);
return "hello";
}
\n

测试运行,访问 http://localhost:8083/t2?name=ajream&age=10&id=10001

\n

\"image-20210904190448500\"

\n
\n

注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下

\n

\"image-20210904190750826\"

\n
\n

表单提交的数据乱码

POST提交数据乱码

创建表单提交页面 web/form.jsp

\n

\"image-20210904195836766\"

\n

添加代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/e/t1" method="post">
<input type="text" name="name">
<input type="submit">
</form>

</body>
</html>

\n

编写后台处理类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.encoding;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class EncodingController {
@PostMapping("/e/t1")
public String test1(String name, Model model){
model.addAttribute("msg", name);
System.out.println(name);
return "test";
}
}

\n

运行测试,先访问表单页 http:localhost:8083/form.jsp, 提交后自动跳转到 localhost:8083/e/t1页面

\n

传入中文数据,返回乱码:

\n

\"image-20210904200350867\"

\n

后台也是:

\n

\"image-20210904200522875\"

\n
提交表单后出现bug \n
\n

提交表单出现404页面

\"image-20210904200902942\"

原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决

\"image-20210904201311953\"

\n
\n
\n

乱码问题解决

web.xml 添加过滤器(这里用的是springMVC提供的)

\n
1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern> <!--注意是 "/*", 不是 "/" -->
</filter-mapping>
\n

注意url-pattern是 “/*”, 不是 “/“

\n
\n

重新运行,前端、后台都显示正常:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
前端后台
\"image-20210904202053181\"\"image-20210904202144232\"
\n
\n
极端情况对get请求支持不好 \n
\n

有些极端情况下.这个过滤器对get的支持不好 .

处理方法 :

修改tomcat配置文件server.xml(设置编码)

1
2
3
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

自定义过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package com.kuang.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}
// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
//取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}
//取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}

在web.xml添加自定义过滤器即可

1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>encoding</filter-name>
<filter-class>【修改为自定义过滤器】</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern> <!--注意是 "/*", 不是 "/" -->
</filter-mapping>

乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!

\n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

获取url提交的数据

项目地址springMVC-06-dataProcess

\n

通过url提交的数据

url的参数名与java方法中的参数名一致(方法名没要求),如下:都是 name

\n

url: http://localhost:8083/hello?name=ajream

\n
1
2
3
4
5
@RequestMapping("/hello")
public String hello(String name){
System.out.println(name);
return "hello";
}
\n

url的参数名与java方法中的参数名不一致, 如下,url中为 username, java方法中为 name

\n

url: http://localhost:8083/hello?username=ajream

\n

使用注解:@RequestParam

\n
1
2
3
4
5
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
System.out.println(name);
return "hello";
}
\n

传入的数据封装成对象

\n

首先创建一个 User

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int id;
private int age;
}

\n

Controller中,添加一个参数 User user,表示传入的参数将会被封装成一个 user 对象

\n
1
2
3
4
5
6
@RequestMapping("/t2")
public String test2(User user, Model model){
System.out.println(user);
model.addAttribute("msg", user);
return "hello";
}
\n

测试运行,访问 http://localhost:8083/t2?name=ajream&age=10&id=10001

\n

\"image-20210904190448500\"

\n
\n

注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下

\n

\"image-20210904190750826\"

\n
\n

表单提交的数据乱码

POST提交数据乱码

创建表单提交页面 web/form.jsp

\n

\"image-20210904195836766\"

\n

添加代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/e/t1" method="post">
<input type="text" name="name">
<input type="submit">
</form>

</body>
</html>

\n

编写后台处理类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.encoding;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class EncodingController {
@PostMapping("/e/t1")
public String test1(String name, Model model){
model.addAttribute("msg", name);
System.out.println(name);
return "test";
}
}

\n

运行测试,先访问表单页 http:localhost:8083/form.jsp, 提交后自动跳转到 localhost:8083/e/t1页面

\n

传入中文数据,返回乱码:

\n

\"image-20210904200350867\"

\n

后台也是:

\n

\"image-20210904200522875\"

\n
提交表单后出现bug \n
\n

提交表单出现404页面

\"image-20210904200902942\"

原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决

\"image-20210904201311953\"

\n
\n
\n

乱码问题解决

web.xml 添加过滤器(这里用的是springMVC提供的)

\n
1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern> <!--注意是 "/*", 不是 "/" -->
</filter-mapping>
\n

注意url-pattern是 “/*”, 不是 “/“

\n
\n

重新运行,前端、后台都显示正常:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
前端后台
\"image-20210904202053181\"\"image-20210904202144232\"
\n
\n
极端情况对get请求支持不好 \n
\n

有些极端情况下.这个过滤器对get的支持不好 .

处理方法 :

修改tomcat配置文件server.xml(设置编码)

1
2
3
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

自定义过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package com.kuang.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}
// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
//取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}
//取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}

在web.xml添加自定义过滤器即可

1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>encoding</filter-name>
<filter-class>【修改为自定义过滤器】</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern> <!--注意是 "/*", 不是 "/" -->
</filter-mapping>

乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!

\n
\n
\n"},{"title":"SpringMVC(二)HelloMVC","description":"SpringMVC的HelloWorld程序","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"a4682555","date":"2021-09-03T02:31:26.000Z","_content":"\n\n\n\n\n[项目地址springMVC-02-hellomvc](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 创建一个普通maven项目 `springmvc-02-hellomvc`\n\n2. 导入依赖\n\n ```xml\n \n \n org.springframework\n spring-webmvc\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.13\n \n \n javax.servlet\n servlet-api\n 2.5\n \n \n javax.servlet.jsp\n jsp-api\n 2.2\n \n \n javax.servlet\n jstl\n 1.2\n \n \n ```\n \n3. 启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)\n\n## 进行开发\n\n### web.xml配置\n\n在【/web/WEB-INF/web.xml】注册 `DispatcherServlet`\n\n```xml\n\n\n\n\n \n springmvc\n org.springframework.web.servlet.DispatcherServlet\n\n\n \n contextConfigLocation\n classpath:springmvc-servlet.xml\n \n\n\n 1\n \n\n\n\n\n \n springmvc\n /\n \n\n```\n\n\n\n### 添加Spring MVC配置文件\n\n\n\n在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:`[servlet-name]-servlet.xml`,在本项目中为 `springmvc-servlet.xml`\n\n添加spring头文件\n\n```xml\n\n\n\n\n```\n\n添加处理映射器\n\n```xml\n\n```\n\n添加处理适配器\n\n```xml\n\n```\n\n添加视图解析器\n\n```xml\n\n\n \n \n \n \n\n```\n\n\n\n### 添加Controller类\n\n在包【com.ajream.controller】编写`HelloController`类,用于具体操作业务\n\n```java\npackage com.ajream.controller;\n\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class HelloController implements Controller {\n// 重写handleRequest方法\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n// 创建模型和视图\n ModelAndView mv = new ModelAndView();\n// 添加对象到模型和视图中\n mv.addObject(\"msg\",\"Hello SpringMVC\");\n// 指定哪个模型视图\n mv.setViewName(\"hello\"); //自动拼接成路径: /WEB-INF/jsp/hello.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后将 `HelloController`类交给SpringIOC,注册到 beans 中\n\n```xml\n\n```\n\n\n\n### 创建视图层\n\n创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title\n\n\n ${msg}\n\n\n\n```\n\n\n\n### 运行Tomcat/访问\n\n配置Tomcat,运行,访问 `http://localhost:8083/hello`\n\n![image-20210903225933025](https://gitee.com/ajream/images/raw/master/img/20210903225934_image-20210903225933025.png)\n\n\n\n\n\n\n\n## 404解决办法\n\n1. 查看控制台输出,看一下是不是缺少了什么jar包。\n\n2. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!\n\n {%folding blue, 点击查看解决办法%}\n\n \n\n 点击工程结构\n\n ![image-20210903224853189](https://gitee.com/ajream/images/raw/master/img/20210903224857_image-20210903224853189.png)\n\n \n\n 选择Artifacts,及自己的project,创建lib文件夹,并导入包\n\n ![image-20210903225354211](https://gitee.com/ajream/images/raw/master/img/20210903225355_image-20210903225354211.png)\n\n {%endfolding%}\n\n3. 重启Tomcat 即可解决!\n\n\n\n","source":"_posts/SpringMVC/springMVC(二)HelloMVC.md","raw":"---\ntitle: SpringMVC(二)HelloMVC\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: SpringMVC的HelloWorld程序\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: a4682555\ndate: 2021-09-03 10:31:26\n---\n\n\n\n\n\n[项目地址springMVC-02-hellomvc](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 创建一个普通maven项目 `springmvc-02-hellomvc`\n\n2. 导入依赖\n\n ```xml\n \n \n org.springframework\n spring-webmvc\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.13\n \n \n javax.servlet\n servlet-api\n 2.5\n \n \n javax.servlet.jsp\n jsp-api\n 2.2\n \n \n javax.servlet\n jstl\n 1.2\n \n \n ```\n \n3. 启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)\n\n## 进行开发\n\n### web.xml配置\n\n在【/web/WEB-INF/web.xml】注册 `DispatcherServlet`\n\n```xml\n\n\n\n\n \n springmvc\n org.springframework.web.servlet.DispatcherServlet\n\n\n \n contextConfigLocation\n classpath:springmvc-servlet.xml\n \n\n\n 1\n \n\n\n\n\n \n springmvc\n /\n \n\n```\n\n\n\n### 添加Spring MVC配置文件\n\n\n\n在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:`[servlet-name]-servlet.xml`,在本项目中为 `springmvc-servlet.xml`\n\n添加spring头文件\n\n```xml\n\n\n\n\n```\n\n添加处理映射器\n\n```xml\n\n```\n\n添加处理适配器\n\n```xml\n\n```\n\n添加视图解析器\n\n```xml\n\n\n \n \n \n \n\n```\n\n\n\n### 添加Controller类\n\n在包【com.ajream.controller】编写`HelloController`类,用于具体操作业务\n\n```java\npackage com.ajream.controller;\n\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class HelloController implements Controller {\n// 重写handleRequest方法\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n// 创建模型和视图\n ModelAndView mv = new ModelAndView();\n// 添加对象到模型和视图中\n mv.addObject(\"msg\",\"Hello SpringMVC\");\n// 指定哪个模型视图\n mv.setViewName(\"hello\"); //自动拼接成路径: /WEB-INF/jsp/hello.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后将 `HelloController`类交给SpringIOC,注册到 beans 中\n\n```xml\n\n```\n\n\n\n### 创建视图层\n\n创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title\n\n\n ${msg}\n\n\n\n```\n\n\n\n### 运行Tomcat/访问\n\n配置Tomcat,运行,访问 `http://localhost:8083/hello`\n\n![image-20210903225933025](https://gitee.com/ajream/images/raw/master/img/20210903225934_image-20210903225933025.png)\n\n\n\n\n\n\n\n## 404解决办法\n\n1. 查看控制台输出,看一下是不是缺少了什么jar包。\n\n2. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!\n\n {%folding blue, 点击查看解决办法%}\n\n \n\n 点击工程结构\n\n ![image-20210903224853189](https://gitee.com/ajream/images/raw/master/img/20210903224857_image-20210903224853189.png)\n\n \n\n 选择Artifacts,及自己的project,创建lib文件夹,并导入包\n\n ![image-20210903225354211](https://gitee.com/ajream/images/raw/master/img/20210903225355_image-20210903225354211.png)\n\n {%endfolding%}\n\n3. 重启Tomcat 即可解决!\n\n\n\n","slug":"SpringMVC/springMVC(二)HelloMVC","published":1,"updated":"2021-09-05T10:33:58.666Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q7004takvecc6ph7mb","content":"

项目地址springMVC-02-hellomvc

\n

环境配置

    \n
  1. 创建一个普通maven项目 springmvc-02-hellomvc

    \n
  2. \n
  3. 导入依赖

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    </dependency>
    </dependencies>
    \n
  4. \n
  5. 启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)

    \n
  6. \n
\n

进行开发

web.xml配置

在【/web/WEB-INF/web.xml】注册 DispatcherServlet

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!-- 注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!-- 关联一个springmvc配置文件,文件名:[servlet-name]-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动级别-->
<load-on-startup>1</load-on-startup>
</servlet>

<!-- 匹配请求 -->
<!-- "/" : 匹配所有请求,除了 ".jsp"-->
<!-- "/*": 匹配 所有请求,包括 ".jsp"-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
\n

添加Spring MVC配置文件

在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:[servlet-name]-servlet.xml,在本项目中为 springmvc-servlet.xml

\n

添加spring头文件

\n
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>
\n

添加处理映射器

\n
1
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
\n

添加处理适配器

\n
1
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
\n

添加视图解析器

\n
1
2
3
4
5
6
7
<!--视图解析器,Dispatcherservlet给他的ModelAndView-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
\n

添加Controller类

在包【com.ajream.controller】编写HelloController类,用于具体操作业务

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ajream.controller;


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//实现Controller接口
public class HelloController implements Controller {
// 重写handleRequest方法
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// 创建模型和视图
ModelAndView mv = new ModelAndView();
// 添加对象到模型和视图中
mv.addObject("msg","Hello SpringMVC");
// 指定哪个模型视图
mv.setViewName("hello"); //自动拼接成路径: /WEB-INF/jsp/hello.jsp
return mv;
}
}

\n

然后将 HelloController类交给SpringIOC,注册到 beans 中

\n
1
<bean id="/hello" class="com.ajream.controller.HelloController"/>
\n

创建视图层

创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容

\n
1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>

\n

运行Tomcat/访问

配置Tomcat,运行,访问 http://localhost:8083/hello

\n

\"image-20210903225933025\"

\n

404解决办法

    \n
  1. 查看控制台输出,看一下是不是缺少了什么jar包。

    \n
  2. \n
  3. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!

    \n
    点击查看解决办法 \n
    \n

    点击工程结构

    \"image-20210903224853189\"

    选择Artifacts,及自己的project,创建lib文件夹,并导入包

    \"image-20210903225354211\"

    \n
    \n
    \n
  4. \n
  5. 重启Tomcat 即可解决!

    \n
  6. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

项目地址springMVC-02-hellomvc

\n

环境配置

    \n
  1. 创建一个普通maven项目 springmvc-02-hellomvc

    \n
  2. \n
  3. 导入依赖

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    </dependency>
    </dependencies>
    \n
  4. \n
  5. 启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)

    \n
  6. \n
\n

进行开发

web.xml配置

在【/web/WEB-INF/web.xml】注册 DispatcherServlet

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!-- 注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!-- 关联一个springmvc配置文件,文件名:[servlet-name]-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动级别-->
<load-on-startup>1</load-on-startup>
</servlet>

<!-- 匹配请求 -->
<!-- "/" : 匹配所有请求,除了 ".jsp"-->
<!-- "/*": 匹配 所有请求,包括 ".jsp"-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
\n

添加Spring MVC配置文件

在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:[servlet-name]-servlet.xml,在本项目中为 springmvc-servlet.xml

\n

添加spring头文件

\n
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>
\n

添加处理映射器

\n
1
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
\n

添加处理适配器

\n
1
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
\n

添加视图解析器

\n
1
2
3
4
5
6
7
<!--视图解析器,Dispatcherservlet给他的ModelAndView-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
\n

添加Controller类

在包【com.ajream.controller】编写HelloController类,用于具体操作业务

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ajream.controller;


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//实现Controller接口
public class HelloController implements Controller {
// 重写handleRequest方法
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// 创建模型和视图
ModelAndView mv = new ModelAndView();
// 添加对象到模型和视图中
mv.addObject("msg","Hello SpringMVC");
// 指定哪个模型视图
mv.setViewName("hello"); //自动拼接成路径: /WEB-INF/jsp/hello.jsp
return mv;
}
}

\n

然后将 HelloController类交给SpringIOC,注册到 beans 中

\n
1
<bean id="/hello" class="com.ajream.controller.HelloController"/>
\n

创建视图层

创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容

\n
1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>

\n

运行Tomcat/访问

配置Tomcat,运行,访问 http://localhost:8083/hello

\n

\"image-20210903225933025\"

\n

404解决办法

    \n
  1. 查看控制台输出,看一下是不是缺少了什么jar包。

    \n
  2. \n
  3. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!

    \n
    点击查看解决办法 \n
    \n

    点击工程结构

    \"image-20210903224853189\"

    选择Artifacts,及自己的project,创建lib文件夹,并导入包

    \"image-20210903225354211\"

    \n
    \n
    \n
  4. \n
  5. 重启Tomcat 即可解决!

    \n
  6. \n
\n"},{"title":"SpringMVC(五)请求结果的跳转方式","description":"请求结果的跳转方式:转发、重定向","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"1eb33081","date":"2021-09-04T07:26:20.000Z","_content":"\n\n\n[项目地址springMVC-05-resultgo](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 理解概念\n\n转发:将某一信息转发到当前访问路径的页面\n\n重定向:从当前页面跳转到另一页面\n\n\n\n> 转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。\n>\n> 重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。\n\n## ModelAndView\n\n设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。\n\n比如:\n\n```java\npublic ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n}\n```\n\n\n\n视图解析器:\n\n```xml\n\n\n \n \n \n \n\n```\n\n\n\n解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:`\"/WEB-INF/jsp/\"`+ `\"test1\"` + `\".jsp\"` --> `\"/WEB-INF/jsp/test.jsp\"`\n\n\n\n\n\n## ServletAPI\n\n通过设置ServletAPI(很少用了) , 不需要视图解析器\n\n1. 通过HttpServletResponse进行输出\n2. 通过HttpServletResponse实现重定向\n3. 通过HttpServletResponse实现转发\n\n\n\n```java\n@Controller\npublic class ResultGo {\n @RequestMapping(\"/result/t1\")\n public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n rsp.getWriter().println(\"Hello,Spring BY servlet API\");\n }\n @RequestMapping(\"/result/t2\")\n public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n// 重定向\n rsp.sendRedirect(\"/index.jsp\");\n }\n @RequestMapping(\"/result/t3\")\n public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {\n //转发\n req.setAttribute(\"msg\",\"/result/t3\");\n req.getRequestDispatcher(\"/WEB-INF/jsp/test.jsp\").forward(req,rsp);\n }\n}\n```\n\n\n\n\n\n## SpringMVC\n\n### 通过SpringMVC来实现转发和重定向-无视图解析器\n\n测试前,需要将视图解析器注释掉\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC {\n @RequestMapping(\"/rsm/t1\")\n public String test1(){\n// 转发一\n return \"/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t2\")\n public String test2(){\n// 转发二\n return \"forward:/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t3\")\n public String test3(){\n// 重定向\n return \"redirect:/index.jsp\";\n\n }\n}\n\n```\n\n转发:\n\n![image-20210904160714983](https://gitee.com/ajream/images/raw/master/img/20210904160718_image-20210904160714983.png)\n\n\n\n\n\n重定向:访问 `http://localhost:8083/rsm/t3` 后重定向到 `http://localhost:8083/index.jsp`\n\n![image-20210904160851220](https://gitee.com/ajream/images/raw/master/img/20210904160852_image-20210904160851220.png)\n\n\n\n\n\n### 使用视图解析器\n\n添加视图解析器\n\n```xml\n\n \n \n\n```\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC2 {\n @RequestMapping(\"/rsm2/t1\")\n public String test1(){\n //转发\n return \"test\"; //通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n }\n @RequestMapping(\"/rsm2/t2\")\n public String test2(){\n //重定向\n return \"redirect:/index.jsp\"; // 使用重定向关键字\"redirect\"后不会经过视图解析器\n //return \"redirect:hello.do\"; //hello.do为另一个请求/\n }\n}\n```\n\n\n\n> 说明:\n>\n> `return \"test\"` 通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n>\n> `return \"redirect:/index.jsp\"` 使用重定向关键字\"redirect\"后不会经过视图解析器\n\n","source":"_posts/SpringMVC/springMVC(五)结果跳转方式.md","raw":"---\ntitle: SpringMVC(五)请求结果的跳转方式\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: 请求结果的跳转方式:转发、重定向\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: 1eb33081\ndate: 2021-09-04 15:26:20\n---\n\n\n\n[项目地址springMVC-05-resultgo](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 理解概念\n\n转发:将某一信息转发到当前访问路径的页面\n\n重定向:从当前页面跳转到另一页面\n\n\n\n> 转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。\n>\n> 重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。\n\n## ModelAndView\n\n设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。\n\n比如:\n\n```java\npublic ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n}\n```\n\n\n\n视图解析器:\n\n```xml\n\n\n \n \n \n \n\n```\n\n\n\n解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:`\"/WEB-INF/jsp/\"`+ `\"test1\"` + `\".jsp\"` --> `\"/WEB-INF/jsp/test.jsp\"`\n\n\n\n\n\n## ServletAPI\n\n通过设置ServletAPI(很少用了) , 不需要视图解析器\n\n1. 通过HttpServletResponse进行输出\n2. 通过HttpServletResponse实现重定向\n3. 通过HttpServletResponse实现转发\n\n\n\n```java\n@Controller\npublic class ResultGo {\n @RequestMapping(\"/result/t1\")\n public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n rsp.getWriter().println(\"Hello,Spring BY servlet API\");\n }\n @RequestMapping(\"/result/t2\")\n public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n// 重定向\n rsp.sendRedirect(\"/index.jsp\");\n }\n @RequestMapping(\"/result/t3\")\n public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {\n //转发\n req.setAttribute(\"msg\",\"/result/t3\");\n req.getRequestDispatcher(\"/WEB-INF/jsp/test.jsp\").forward(req,rsp);\n }\n}\n```\n\n\n\n\n\n## SpringMVC\n\n### 通过SpringMVC来实现转发和重定向-无视图解析器\n\n测试前,需要将视图解析器注释掉\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC {\n @RequestMapping(\"/rsm/t1\")\n public String test1(){\n// 转发一\n return \"/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t2\")\n public String test2(){\n// 转发二\n return \"forward:/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t3\")\n public String test3(){\n// 重定向\n return \"redirect:/index.jsp\";\n\n }\n}\n\n```\n\n转发:\n\n![image-20210904160714983](https://gitee.com/ajream/images/raw/master/img/20210904160718_image-20210904160714983.png)\n\n\n\n\n\n重定向:访问 `http://localhost:8083/rsm/t3` 后重定向到 `http://localhost:8083/index.jsp`\n\n![image-20210904160851220](https://gitee.com/ajream/images/raw/master/img/20210904160852_image-20210904160851220.png)\n\n\n\n\n\n### 使用视图解析器\n\n添加视图解析器\n\n```xml\n\n \n \n\n```\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC2 {\n @RequestMapping(\"/rsm2/t1\")\n public String test1(){\n //转发\n return \"test\"; //通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n }\n @RequestMapping(\"/rsm2/t2\")\n public String test2(){\n //重定向\n return \"redirect:/index.jsp\"; // 使用重定向关键字\"redirect\"后不会经过视图解析器\n //return \"redirect:hello.do\"; //hello.do为另一个请求/\n }\n}\n```\n\n\n\n> 说明:\n>\n> `return \"test\"` 通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n>\n> `return \"redirect:/index.jsp\"` 使用重定向关键字\"redirect\"后不会经过视图解析器\n\n","slug":"SpringMVC/springMVC(五)结果跳转方式","published":1,"updated":"2021-09-05T10:34:11.953Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q9004wakve8yyz5ix5","content":"

项目地址springMVC-05-resultgo

\n

理解概念

转发:将某一信息转发到当前访问路径的页面

\n

重定向:从当前页面跳转到另一页面

\n
\n

转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。

\n

重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。

\n
\n

ModelAndView

设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。

\n

比如:

\n
1
2
3
4
5
6
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1");
return mv;
}
\n

视图解析器:

\n
1
2
3
4
5
6
7
8
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
\n

解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:"/WEB-INF/jsp/"+ "test1" + ".jsp" —> "/WEB-INF/jsp/test.jsp"

\n

ServletAPI

通过设置ServletAPI(很少用了) , 不需要视图解析器

\n
    \n
  1. 通过HttpServletResponse进行输出
  2. \n
  3. 通过HttpServletResponse实现重定向
  4. \n
  5. 通过HttpServletResponse实现转发
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Controller
public class ResultGo {
@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
rsp.getWriter().println("Hello,Spring BY servlet API");
}
@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
// 重定向
rsp.sendRedirect("/index.jsp");
}
@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
//转发
req.setAttribute("msg","/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
}
}
\n

SpringMVC

通过SpringMVC来实现转发和重定向-无视图解析器

测试前,需要将视图解析器注释掉

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Controller
public class ResultSpringMVC {
@RequestMapping("/rsm/t1")
public String test1(){
// 转发一
return "/index.jsp";
}

@RequestMapping("/rsm/t2")
public String test2(){
// 转发二
return "forward:/index.jsp";
}

@RequestMapping("/rsm/t3")
public String test3(){
// 重定向
return "redirect:/index.jsp";

}
}

\n

转发:

\n

\"image-20210904160714983\"

\n

重定向:访问 http://localhost:8083/rsm/t3 后重定向到 http://localhost:8083/index.jsp

\n

\"image-20210904160851220\"

\n

使用视图解析器

添加视图解析器

\n
1
2
3
4
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
//转发
return "test"; //通过视图解析后得到路径 /WEB-INF/jsp/test.jsp
}
@RequestMapping("/rsm2/t2")
public String test2(){
//重定向
return "redirect:/index.jsp"; // 使用重定向关键字"redirect"后不会经过视图解析器
//return "redirect:hello.do"; //hello.do为另一个请求/
}
}
\n
\n

说明:

\n

return "test" 通过视图解析后得到路径 /WEB-INF/jsp/test.jsp

\n

return "redirect:/index.jsp" 使用重定向关键字”redirect”后不会经过视图解析器

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

项目地址springMVC-05-resultgo

\n

理解概念

转发:将某一信息转发到当前访问路径的页面

\n

重定向:从当前页面跳转到另一页面

\n
\n

转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。

\n

重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。

\n
\n

ModelAndView

设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。

\n

比如:

\n
1
2
3
4
5
6
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1");
return mv;
}
\n

视图解析器:

\n
1
2
3
4
5
6
7
8
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
\n

解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:"/WEB-INF/jsp/"+ "test1" + ".jsp" —> "/WEB-INF/jsp/test.jsp"

\n

ServletAPI

通过设置ServletAPI(很少用了) , 不需要视图解析器

\n
    \n
  1. 通过HttpServletResponse进行输出
  2. \n
  3. 通过HttpServletResponse实现重定向
  4. \n
  5. 通过HttpServletResponse实现转发
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Controller
public class ResultGo {
@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
rsp.getWriter().println("Hello,Spring BY servlet API");
}
@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
// 重定向
rsp.sendRedirect("/index.jsp");
}
@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
//转发
req.setAttribute("msg","/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
}
}
\n

SpringMVC

通过SpringMVC来实现转发和重定向-无视图解析器

测试前,需要将视图解析器注释掉

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Controller
public class ResultSpringMVC {
@RequestMapping("/rsm/t1")
public String test1(){
// 转发一
return "/index.jsp";
}

@RequestMapping("/rsm/t2")
public String test2(){
// 转发二
return "forward:/index.jsp";
}

@RequestMapping("/rsm/t3")
public String test3(){
// 重定向
return "redirect:/index.jsp";

}
}

\n

转发:

\n

\"image-20210904160714983\"

\n

重定向:访问 http://localhost:8083/rsm/t3 后重定向到 http://localhost:8083/index.jsp

\n

\"image-20210904160851220\"

\n

使用视图解析器

添加视图解析器

\n
1
2
3
4
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
//转发
return "test"; //通过视图解析后得到路径 /WEB-INF/jsp/test.jsp
}
@RequestMapping("/rsm2/t2")
public String test2(){
//重定向
return "redirect:/index.jsp"; // 使用重定向关键字"redirect"后不会经过视图解析器
//return "redirect:hello.do"; //hello.do为另一个请求/
}
}
\n
\n

说明:

\n

return "test" 通过视图解析后得到路径 /WEB-INF/jsp/test.jsp

\n

return "redirect:/index.jsp" 使用重定向关键字”redirect”后不会经过视图解析器

\n
\n"},{"title":"SpringMVC(四)Controller与RestFul","description":"Controller详解","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"57ac54fe","date":"2021-09-04T02:31:11.000Z","_content":"\n\n\n\n\n[项目地址springMVC-04-controller](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 创建工程\n\n首先创建一个项目,运行观察效果\n\n\n\n导入依赖\n\n**配置web.xml**\n\n```xml\n\n\n\n \n \n SpringMVC\n org.springframework.web.servlet.DispatcherServlet\n\n \n \n contextConfigLocation\n \n classpath:springmvc-servlet.xml\n \n\n \n 1\n \n\n \n \n SpringMVC\n /\n \n\n\n```\n\n\n\n**配置SpringMVC配置文件**\n\n创建 `resources/springmvc-servlet.xml`\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n\n\n```\n\n\n\n\n\n创建 `ControllerTest1`\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n }\n}\n\n```\n\n\n\n注册 bean\n\n```xml\n\n\n```\n\n\n\n\n\n配置tomcat,添加lib,运行,访问 `http://localhost:8083/t1`\n\n\n\n![image-20210904104223512](https://gitee.com/ajream/images/raw/master/img/20210904104231_image-20210904104223512.png)\n\n\n\n\n\n## 解释\n\n\n\n### 控制器Controller\n\n- 控制器复杂提供访问应用程序的行为,通常通过接口定义注解定义两种方法实现\n- 控制器负责解析用户的请求并将其转换为一个模型\n- 在Spring MVC中一个控制器类可以包含多个方法\n- 在Spring MVC中,对于Controller的配置方式有很多种\n\n\n\n\n\n### 实现Controller\n\n通常通过接口定义注解定义两种方法实现\n\n#### 通过接口实现\n\nController是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;\n\n```java\n//实现该接口的类获得控制器功能\npublic interface Controller {\n //处理请求且返回一个模型与视图对象\n ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;\n}\n```\n\n\n\n例如上面的例子:\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n //返回一个模型视图对象\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\"); //WEB-INF/jsp/test1.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后去spring MVC的配置文件注册bean\n\n```xml\n\n```\n\n\n\n> 说明:\n>\n> 实现接口Controller定义控制器是较老的办法\n>\n> 缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦\n\n\n\n\n\n#### 通过注解实现\n\n\n\n- `@Controller`注解类型用于声明Spring类的实例是一个控制器\n\n- Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描\n\n ```xml\n \n \n ```\n\n\n\n例如,创建一个 `ControllerTest2`,使用注解来实现Controller\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n//@Controller注解的类会自动添加到Spring上下文中\n@Controller\npublic class ControllerTest2{\n //映射访问路径\n @RequestMapping(\"/t2\")\n public String index(Model model){\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"这是ControllerTest2\");\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n添加注解支持\n\n```xml\n\n```\n\n运行,访问:`http://localhost:8083/t2`\n\n\n\n![image-20210904110819742](https://gitee.com/ajream/images/raw/master/img/20210904110823_image-20210904110819742.png)\n\n\n\n\n\n> 可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。\n\n\n\n\n\n\n\n### 注解RequestMapping\n\n`@RequestMapping`注解用于映射url到控制器类或一个特定的处理程序方法。\n\n可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。\n\n\n\n例如\n\n只用于方法上,访问路径是 `http://localhost:8080/h1`\n\n```java\n@Controller\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n同时用于类和方法上,访问路径是 `http://localhost:8080/admin/h1`\n\n```java\n@Controller\n@RequestMapping(\"/admin\")\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n\n\n## RestFul 风格\n\n\n\nRestful就是一个资源定位资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。\n\n\n\nRestFul风格即对访问路径进行的优化\n\n比如:如果需要传参,传统访问路径可能是:`http://localhost:8080/admin?name=ajream&password=123456`\n\n而RestFul风格的访问路径是:`http://localhost:8080/admin/ajream/123456`\n\n\n\n接下来用个例子来说明:\n\n新建一个类 `RestFulController`\n\n```java\n@Controller\npublic class RestFulController {\n\n}\n```\n\n在Spring MVC中可以使用 `@PathVariable` 注解,让方法参数的值对应绑定到一个URI模板变量上。\n\n```java\n@Controller\npublic class RestFulController {\n //映射访问路径\n @RequestMapping(\"/commit/{p1}/{p2}\")\n public String index(@PathVariable int p1, @PathVariable int p2, Model model){\n int result = p1+p2;\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"Result:\"+result);\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n{%note info flat%}\n\n注意 p1, p2 均为int类型变量,如果传入 `a`, `b` ...这些字符就会出错\n\n{%endnote%}\n\n\n\n运行测试:\n\n\"image-20210904145334321\"\n\n\n\n\"image-20210904145629896\"\n\n\n\n## 使用method属性指定请求方式\n\n\n\n请求方式常用的有:get、post、...\n\n在注解 `@RequestMapping` 使用`mothod` 属性可以指定请求方式\n\n如:\n\n```java\n@Controller\npublic class RestFulController {\n\t//映射访问路径,必须是POST请求\n @RequestMapping(value = \"/t3/{a}/{b}\", method = RequestMethod.POST)\n public String test(@PathVariable int a, @PathVariable int b, Model model){\n\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n\n return \"test1\";\n\n }\n}\n```\n\n使用浏览器地址栏进行访问默认是Get请求,会报错405:\n\n![image-20210904150415014](https://gitee.com/ajream/images/raw/master/img/20210904150419_image-20210904150415014.png)\n\n\n\n可以同时使用多种请求方式:\n\n```java\n@RequestMapping(value = \"/t3/{a}/{b}\", method = {RequestMethod.POST, RequestMethod.GET})\n```\n\n\n\n\n\n也可以使用对应请求方式的注解(可以把它们叫做组合注解):\n\n```java\n@GetMapping(\"/t1\") \t\t//等价于 @RequestMapping(value=\"t1\", method=RequestMethod.GET)\n@PostMapping(\"/t1\")\n@PutMapping(\"/t1\")\n@DeleteMapping(\"/t1\")\n@PatchMapping(\"/t1\")\n```\n\n例如\n\n```java\n@GetMapping(\"/t3/{a}/{b}\")\npublic String test(@PathVariable int a, @PathVariable int b, Model model){\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n return \"test1\";\n\n}\n```\n\n","source":"_posts/SpringMVC/springMVC(四)Controller与restful.md","raw":"---\ntitle: SpringMVC(四)Controller与RestFul\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: Controller详解\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: 57ac54fe\ndate: 2021-09-04 10:31:11\n---\n\n\n\n\n\n[项目地址springMVC-04-controller](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 创建工程\n\n首先创建一个项目,运行观察效果\n\n\n\n导入依赖\n\n**配置web.xml**\n\n```xml\n\n\n\n \n \n SpringMVC\n org.springframework.web.servlet.DispatcherServlet\n\n \n \n contextConfigLocation\n \n classpath:springmvc-servlet.xml\n \n\n \n 1\n \n\n \n \n SpringMVC\n /\n \n\n\n```\n\n\n\n**配置SpringMVC配置文件**\n\n创建 `resources/springmvc-servlet.xml`\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n\n\n```\n\n\n\n\n\n创建 `ControllerTest1`\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n }\n}\n\n```\n\n\n\n注册 bean\n\n```xml\n\n\n```\n\n\n\n\n\n配置tomcat,添加lib,运行,访问 `http://localhost:8083/t1`\n\n\n\n![image-20210904104223512](https://gitee.com/ajream/images/raw/master/img/20210904104231_image-20210904104223512.png)\n\n\n\n\n\n## 解释\n\n\n\n### 控制器Controller\n\n- 控制器复杂提供访问应用程序的行为,通常通过接口定义注解定义两种方法实现\n- 控制器负责解析用户的请求并将其转换为一个模型\n- 在Spring MVC中一个控制器类可以包含多个方法\n- 在Spring MVC中,对于Controller的配置方式有很多种\n\n\n\n\n\n### 实现Controller\n\n通常通过接口定义注解定义两种方法实现\n\n#### 通过接口实现\n\nController是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;\n\n```java\n//实现该接口的类获得控制器功能\npublic interface Controller {\n //处理请求且返回一个模型与视图对象\n ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;\n}\n```\n\n\n\n例如上面的例子:\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n //返回一个模型视图对象\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\"); //WEB-INF/jsp/test1.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后去spring MVC的配置文件注册bean\n\n```xml\n\n```\n\n\n\n> 说明:\n>\n> 实现接口Controller定义控制器是较老的办法\n>\n> 缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦\n\n\n\n\n\n#### 通过注解实现\n\n\n\n- `@Controller`注解类型用于声明Spring类的实例是一个控制器\n\n- Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描\n\n ```xml\n \n \n ```\n\n\n\n例如,创建一个 `ControllerTest2`,使用注解来实现Controller\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n//@Controller注解的类会自动添加到Spring上下文中\n@Controller\npublic class ControllerTest2{\n //映射访问路径\n @RequestMapping(\"/t2\")\n public String index(Model model){\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"这是ControllerTest2\");\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n添加注解支持\n\n```xml\n\n```\n\n运行,访问:`http://localhost:8083/t2`\n\n\n\n![image-20210904110819742](https://gitee.com/ajream/images/raw/master/img/20210904110823_image-20210904110819742.png)\n\n\n\n\n\n> 可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。\n\n\n\n\n\n\n\n### 注解RequestMapping\n\n`@RequestMapping`注解用于映射url到控制器类或一个特定的处理程序方法。\n\n可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。\n\n\n\n例如\n\n只用于方法上,访问路径是 `http://localhost:8080/h1`\n\n```java\n@Controller\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n同时用于类和方法上,访问路径是 `http://localhost:8080/admin/h1`\n\n```java\n@Controller\n@RequestMapping(\"/admin\")\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n\n\n## RestFul 风格\n\n\n\nRestful就是一个资源定位资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。\n\n\n\nRestFul风格即对访问路径进行的优化\n\n比如:如果需要传参,传统访问路径可能是:`http://localhost:8080/admin?name=ajream&password=123456`\n\n而RestFul风格的访问路径是:`http://localhost:8080/admin/ajream/123456`\n\n\n\n接下来用个例子来说明:\n\n新建一个类 `RestFulController`\n\n```java\n@Controller\npublic class RestFulController {\n\n}\n```\n\n在Spring MVC中可以使用 `@PathVariable` 注解,让方法参数的值对应绑定到一个URI模板变量上。\n\n```java\n@Controller\npublic class RestFulController {\n //映射访问路径\n @RequestMapping(\"/commit/{p1}/{p2}\")\n public String index(@PathVariable int p1, @PathVariable int p2, Model model){\n int result = p1+p2;\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"Result:\"+result);\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n{%note info flat%}\n\n注意 p1, p2 均为int类型变量,如果传入 `a`, `b` ...这些字符就会出错\n\n{%endnote%}\n\n\n\n运行测试:\n\n\"image-20210904145334321\"\n\n\n\n\"image-20210904145629896\"\n\n\n\n## 使用method属性指定请求方式\n\n\n\n请求方式常用的有:get、post、...\n\n在注解 `@RequestMapping` 使用`mothod` 属性可以指定请求方式\n\n如:\n\n```java\n@Controller\npublic class RestFulController {\n\t//映射访问路径,必须是POST请求\n @RequestMapping(value = \"/t3/{a}/{b}\", method = RequestMethod.POST)\n public String test(@PathVariable int a, @PathVariable int b, Model model){\n\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n\n return \"test1\";\n\n }\n}\n```\n\n使用浏览器地址栏进行访问默认是Get请求,会报错405:\n\n![image-20210904150415014](https://gitee.com/ajream/images/raw/master/img/20210904150419_image-20210904150415014.png)\n\n\n\n可以同时使用多种请求方式:\n\n```java\n@RequestMapping(value = \"/t3/{a}/{b}\", method = {RequestMethod.POST, RequestMethod.GET})\n```\n\n\n\n\n\n也可以使用对应请求方式的注解(可以把它们叫做组合注解):\n\n```java\n@GetMapping(\"/t1\") \t\t//等价于 @RequestMapping(value=\"t1\", method=RequestMethod.GET)\n@PostMapping(\"/t1\")\n@PutMapping(\"/t1\")\n@DeleteMapping(\"/t1\")\n@PatchMapping(\"/t1\")\n```\n\n例如\n\n```java\n@GetMapping(\"/t3/{a}/{b}\")\npublic String test(@PathVariable int a, @PathVariable int b, Model model){\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n return \"test1\";\n\n}\n```\n\n","slug":"SpringMVC/springMVC(四)Controller与restful","published":1,"updated":"2021-09-05T10:34:07.415Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qb004yakve5qk6a50r","content":"

项目地址springMVC-04-controller

\n

创建工程

首先创建一个项目,运行观察效果

\n

导入依赖

\n

配置web.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--1.注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--在resources新建springmvc-servlet.xml文件-->
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
\n

配置SpringMVC配置文件

\n

创建 resources/springmvc-servlet.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>


</beans>
\n

创建 ControllerTest1

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ControllerTest1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1");
return mv;
}
}

\n

注册 bean

\n
1
2
<!--注意“/t1” 的斜杠不能少-->
<bean id="/t1" class="com.ajream.controller.ControllerTest1"/>
\n

配置tomcat,添加lib,运行,访问 http://localhost:8083/t1

\n

\"image-20210904104223512\"

\n

解释

控制器Controller

    \n
  • 控制器复杂提供访问应用程序的行为,通常通过接口定义注解定义两种方法实现
  • \n
  • 控制器负责解析用户的请求并将其转换为一个模型
  • \n
  • 在Spring MVC中一个控制器类可以包含多个方法
  • \n
  • 在Spring MVC中,对于Controller的配置方式有很多种
  • \n
\n

实现Controller

通常通过接口定义注解定义两种方法实现

\n

通过接口实现

Controller是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;

\n
1
2
3
4
5
//实现该接口的类获得控制器功能
public interface Controller {
//处理请求且返回一个模型与视图对象
ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}
\n

例如上面的例子:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.ajream.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//实现Controller接口
public class ControllerTest1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1"); //WEB-INF/jsp/test1.jsp
return mv;
}
}

\n

然后去spring MVC的配置文件注册bean

\n
1
<bean name="/t1" class="com.kuang.controller.ControllerTest1"/>
\n
\n

说明:

\n

实现接口Controller定义控制器是较老的办法

\n

缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦

\n
\n

通过注解实现

    \n
  • @Controller注解类型用于声明Spring类的实例是一个控制器

    \n
  • \n
  • Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描

    \n
    1
    2
    <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
    <context:component-scan base-package="com.ajream.controller"/>
    \n
  • \n
\n

例如,创建一个 ControllerTest2,使用注解来实现Controller

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

//@Controller注解的类会自动添加到Spring上下文中
@Controller
public class ControllerTest2{
//映射访问路径
@RequestMapping("/t2")
public String index(Model model){
//Spring MVC会自动实例化一个Model对象用于向视图中传值
model.addAttribute("msg", "这是ControllerTest2");
//返回视图位置
return "test1";
}
}
\n

添加注解支持

\n
1
<context:component-scan base-package="com.ajream.controller"/>
\n

运行,访问:http://localhost:8083/t2

\n

\"image-20210904110819742\"

\n
\n

可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。

\n
\n

注解RequestMapping

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。

\n

可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

\n

例如

\n

只用于方法上,访问路径是 http://localhost:8080/h1

\n
1
2
3
4
5
6
7
@Controller
public class TestController {
@RequestMapping("/h1")
public String test(){
return "test";
}
}
\n

同时用于类和方法上,访问路径是 http://localhost:8080/admin/h1

\n
1
2
3
4
5
6
7
8
@Controller
@RequestMapping("/admin")
public class TestController {
@RequestMapping("/h1")
public String test(){
return "test";
}
}
\n

RestFul 风格

Restful就是一个资源定位资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

\n

RestFul风格即对访问路径进行的优化

\n

比如:如果需要传参,传统访问路径可能是:http://localhost:8080/admin?name=ajream&password=123456

\n

而RestFul风格的访问路径是:http://localhost:8080/admin/ajream/123456

\n

接下来用个例子来说明:

\n

新建一个类 RestFulController

\n
1
2
3
4
@Controller
public class RestFulController {

}
\n

在Spring MVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上。

\n
1
2
3
4
5
6
7
8
9
10
11
12
@Controller
public class RestFulController {
//映射访问路径
@RequestMapping("/commit/{p1}/{p2}")
public String index(@PathVariable int p1, @PathVariable int p2, Model model){
int result = p1+p2;
//Spring MVC会自动实例化一个Model对象用于向视图中传值
model.addAttribute("msg", "Result:"+result);
//返回视图位置
return "test1";
}
}
\n

注意 p1, p2 均为int类型变量,如果传入 a, b …这些字符就会出错

\n
\n

运行测试:

\n

\"image-20210904145334321\"

\n

\"image-20210904145629896\"

\n

使用method属性指定请求方式

请求方式常用的有:get、post、…

\n

在注解 @RequestMapping 使用mothod 属性可以指定请求方式

\n

如:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class RestFulController {
\t//映射访问路径,必须是POST请求
@RequestMapping(value = "/t3/{a}/{b}", method = RequestMethod.POST)
public String test(@PathVariable int a, @PathVariable int b, Model model){

int r = a + b;
model.addAttribute("msg","Result: " + r);

return "test1";

}
}
\n

使用浏览器地址栏进行访问默认是Get请求,会报错405:

\n

\"image-20210904150415014\"

\n

可以同时使用多种请求方式:

\n
1
@RequestMapping(value = "/t3/{a}/{b}", method = {RequestMethod.POST, RequestMethod.GET})
\n

也可以使用对应请求方式的注解(可以把它们叫做组合注解):

\n
1
2
3
4
5
@GetMapping("/t1")    \t\t//等价于 @RequestMapping(value="t1", method=RequestMethod.GET)
@PostMapping("/t1")
@PutMapping("/t1")
@DeleteMapping("/t1")
@PatchMapping("/t1")
\n

例如

\n
1
2
3
4
5
6
7
@GetMapping("/t3/{a}/{b}")
public String test(@PathVariable int a, @PathVariable int b, Model model){
int r = a + b;
model.addAttribute("msg","Result: " + r);
return "test1";

}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

项目地址springMVC-04-controller

\n

创建工程

首先创建一个项目,运行观察效果

\n

导入依赖

\n

配置web.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--1.注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--在resources新建springmvc-servlet.xml文件-->
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
\n

配置SpringMVC配置文件

\n

创建 resources/springmvc-servlet.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>


</beans>
\n

创建 ControllerTest1

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ControllerTest1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1");
return mv;
}
}

\n

注册 bean

\n
1
2
<!--注意“/t1” 的斜杠不能少-->
<bean id="/t1" class="com.ajream.controller.ControllerTest1"/>
\n

配置tomcat,添加lib,运行,访问 http://localhost:8083/t1

\n

\"image-20210904104223512\"

\n

解释

控制器Controller

    \n
  • 控制器复杂提供访问应用程序的行为,通常通过接口定义注解定义两种方法实现
  • \n
  • 控制器负责解析用户的请求并将其转换为一个模型
  • \n
  • 在Spring MVC中一个控制器类可以包含多个方法
  • \n
  • 在Spring MVC中,对于Controller的配置方式有很多种
  • \n
\n

实现Controller

通常通过接口定义注解定义两种方法实现

\n

通过接口实现

Controller是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;

\n
1
2
3
4
5
//实现该接口的类获得控制器功能
public interface Controller {
//处理请求且返回一个模型与视图对象
ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}
\n

例如上面的例子:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.ajream.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//实现Controller接口
public class ControllerTest1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1"); //WEB-INF/jsp/test1.jsp
return mv;
}
}

\n

然后去spring MVC的配置文件注册bean

\n
1
<bean name="/t1" class="com.kuang.controller.ControllerTest1"/>
\n
\n

说明:

\n

实现接口Controller定义控制器是较老的办法

\n

缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦

\n
\n

通过注解实现

    \n
  • @Controller注解类型用于声明Spring类的实例是一个控制器

    \n
  • \n
  • Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描

    \n
    1
    2
    <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
    <context:component-scan base-package="com.ajream.controller"/>
    \n
  • \n
\n

例如,创建一个 ControllerTest2,使用注解来实现Controller

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

//@Controller注解的类会自动添加到Spring上下文中
@Controller
public class ControllerTest2{
//映射访问路径
@RequestMapping("/t2")
public String index(Model model){
//Spring MVC会自动实例化一个Model对象用于向视图中传值
model.addAttribute("msg", "这是ControllerTest2");
//返回视图位置
return "test1";
}
}
\n

添加注解支持

\n
1
<context:component-scan base-package="com.ajream.controller"/>
\n

运行,访问:http://localhost:8083/t2

\n

\"image-20210904110819742\"

\n
\n

可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。

\n
\n

注解RequestMapping

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。

\n

可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

\n

例如

\n

只用于方法上,访问路径是 http://localhost:8080/h1

\n
1
2
3
4
5
6
7
@Controller
public class TestController {
@RequestMapping("/h1")
public String test(){
return "test";
}
}
\n

同时用于类和方法上,访问路径是 http://localhost:8080/admin/h1

\n
1
2
3
4
5
6
7
8
@Controller
@RequestMapping("/admin")
public class TestController {
@RequestMapping("/h1")
public String test(){
return "test";
}
}
\n

RestFul 风格

Restful就是一个资源定位资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

\n

RestFul风格即对访问路径进行的优化

\n

比如:如果需要传参,传统访问路径可能是:http://localhost:8080/admin?name=ajream&password=123456

\n

而RestFul风格的访问路径是:http://localhost:8080/admin/ajream/123456

\n

接下来用个例子来说明:

\n

新建一个类 RestFulController

\n
1
2
3
4
@Controller
public class RestFulController {

}
\n

在Spring MVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上。

\n
1
2
3
4
5
6
7
8
9
10
11
12
@Controller
public class RestFulController {
//映射访问路径
@RequestMapping("/commit/{p1}/{p2}")
public String index(@PathVariable int p1, @PathVariable int p2, Model model){
int result = p1+p2;
//Spring MVC会自动实例化一个Model对象用于向视图中传值
model.addAttribute("msg", "Result:"+result);
//返回视图位置
return "test1";
}
}
\n

注意 p1, p2 均为int类型变量,如果传入 a, b …这些字符就会出错

\n
\n

运行测试:

\n

\"image-20210904145334321\"

\n

\"image-20210904145629896\"

\n

使用method属性指定请求方式

请求方式常用的有:get、post、…

\n

在注解 @RequestMapping 使用mothod 属性可以指定请求方式

\n

如:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class RestFulController {
\t//映射访问路径,必须是POST请求
@RequestMapping(value = "/t3/{a}/{b}", method = RequestMethod.POST)
public String test(@PathVariable int a, @PathVariable int b, Model model){

int r = a + b;
model.addAttribute("msg","Result: " + r);

return "test1";

}
}
\n

使用浏览器地址栏进行访问默认是Get请求,会报错405:

\n

\"image-20210904150415014\"

\n

可以同时使用多种请求方式:

\n
1
@RequestMapping(value = "/t3/{a}/{b}", method = {RequestMethod.POST, RequestMethod.GET})
\n

也可以使用对应请求方式的注解(可以把它们叫做组合注解):

\n
1
2
3
4
5
@GetMapping("/t1")    \t\t//等价于 @RequestMapping(value="t1", method=RequestMethod.GET)
@PostMapping("/t1")
@PutMapping("/t1")
@DeleteMapping("/t1")
@PatchMapping("/t1")
\n

例如

\n
1
2
3
4
5
6
7
@GetMapping("/t3/{a}/{b}")
public String test(@PathVariable int a, @PathVariable int b, Model model){
int r = a + b;
model.addAttribute("msg","Result: " + r);
return "test1";

}
\n"},{"title":"1-Servlet","abbrlink":"345d8e17","date":"2021-04-12T02:44:20.000Z","description":"Servlet举例介绍","_content":"\n\n# Servlet\n\n## servlet技术\n\n1. Servlet是Java EE规范之一,规范即接口\n2. 是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器\n3. 是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端\n4. Servlet没有 `main()`方法,由系统自动调用\n\n\n\n## IDEA创建servlet\n\n\"在这里插入图片描述\"\n\n\n\n\"image-20210502163733514\"\n\n\n\n\"image-20210502163838549\"\n\n\n\n\n\n## 手动实现Servlet程序\n\n1. 编写类实现Servlet规范,有3种方式:\n\n 1. 继承 `HttpServlet`类\n\n ```java\n public class HelloServlet extends HttpServlet {\n\n }\n ```\n\n 2. 实现 `Servlet`接口\n\n ```java\n package com.example.servlet01;\n \n import javax.servlet.*;\n import java.io.IOException;\n \n public class Servlet01 implements Servlet {\n @Override\n public void init(ServletConfig servletConfig) throws ServletException {\n \n }\n \n @Override\n public ServletConfig getServletConfig() {\n return null;\n }\n \n @Override\n public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {\n \t\tSystem.out.println(\"实现Servlet接口\");\n }\n \n @Override\n public String getServletInfo() {\n return null;\n }\n \n @Override\n public void destroy() {\n \n }\n }\n \n ```\n\n \n\n2. 实现 `service()`方法,处理请求,响应数据\n\n 【快捷键 ctrl + o 可以重写方法或实现接口】\n\n ```java\n public class HelloServlet extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n //控制台打印\n System.out.println(\"Hello Servlet\");\n \n //通过流输出数据到浏览器\n resp.getWriter().write(\"hello servlet!\");\n }\n }\n ```\n\n3. 设置注解`@WebServlet`,指定访问路径\n\n ```java\n @WebServlet(name=\"helloServlet\", value = \"/ser01\") //name可以不设置\n ```\n\n\n\n\n> 在第二步中,也可以不重写 `Service()`方法,而是更具体些,重写 `doGet()`、`doPost`方法;\n>\n> 根据浏览器请求方式来进行不同处理\n>\n> 但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少\n\n\n\n## Servlet工作流程\n\nurl`http://localhost:8083/servlet01/ser01`含义:\n\n> - 服务器:`http://localhost:8083`\n> - 项目:`/servlet01`\n> - 资源:`/ser01`\n\n","source":"_posts/javaEE/1-Servlet.md","raw":"---\ntitle: 1-Servlet\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: 345d8e17\ndate: 2021-04-12 10:44:20\ndescription: Servlet举例介绍\n---\n\n\n# Servlet\n\n## servlet技术\n\n1. Servlet是Java EE规范之一,规范即接口\n2. 是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器\n3. 是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端\n4. Servlet没有 `main()`方法,由系统自动调用\n\n\n\n## IDEA创建servlet\n\n\"在这里插入图片描述\"\n\n\n\n\"image-20210502163733514\"\n\n\n\n\"image-20210502163838549\"\n\n\n\n\n\n## 手动实现Servlet程序\n\n1. 编写类实现Servlet规范,有3种方式:\n\n 1. 继承 `HttpServlet`类\n\n ```java\n public class HelloServlet extends HttpServlet {\n\n }\n ```\n\n 2. 实现 `Servlet`接口\n\n ```java\n package com.example.servlet01;\n \n import javax.servlet.*;\n import java.io.IOException;\n \n public class Servlet01 implements Servlet {\n @Override\n public void init(ServletConfig servletConfig) throws ServletException {\n \n }\n \n @Override\n public ServletConfig getServletConfig() {\n return null;\n }\n \n @Override\n public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {\n \t\tSystem.out.println(\"实现Servlet接口\");\n }\n \n @Override\n public String getServletInfo() {\n return null;\n }\n \n @Override\n public void destroy() {\n \n }\n }\n \n ```\n\n \n\n2. 实现 `service()`方法,处理请求,响应数据\n\n 【快捷键 ctrl + o 可以重写方法或实现接口】\n\n ```java\n public class HelloServlet extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n //控制台打印\n System.out.println(\"Hello Servlet\");\n \n //通过流输出数据到浏览器\n resp.getWriter().write(\"hello servlet!\");\n }\n }\n ```\n\n3. 设置注解`@WebServlet`,指定访问路径\n\n ```java\n @WebServlet(name=\"helloServlet\", value = \"/ser01\") //name可以不设置\n ```\n\n\n\n\n> 在第二步中,也可以不重写 `Service()`方法,而是更具体些,重写 `doGet()`、`doPost`方法;\n>\n> 根据浏览器请求方式来进行不同处理\n>\n> 但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少\n\n\n\n## Servlet工作流程\n\nurl`http://localhost:8083/servlet01/ser01`含义:\n\n> - 服务器:`http://localhost:8083`\n> - 项目:`/servlet01`\n> - 资源:`/ser01`\n\n","slug":"javaEE/1-Servlet","published":1,"updated":"2021-09-10T03:08:58.332Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qc0051akve1nql6qt7","content":"

Servlet

servlet技术

    \n
  1. Servlet是Java EE规范之一,规范即接口
  2. \n
  3. 是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器
  4. \n
  5. 是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端
  6. \n
  7. Servlet没有 main()方法,由系统自动调用
  8. \n
\n

IDEA创建servlet

\"在这里插入图片描述\"

\n

\"image-20210502163733514\"

\n

\"image-20210502163838549\"

\n

手动实现Servlet程序

    \n
  1. 编写类实现Servlet规范,有3种方式:

    \n
      \n
    1. 继承 HttpServlet

      \n
      1
      2
      3
      public class HelloServlet extends HttpServlet {

      }
      \n
    2. \n
    3. 实现 Servlet接口

      \n
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      package com.example.servlet01;

      import javax.servlet.*;
      import java.io.IOException;

      public class Servlet01 implements Servlet {
      @Override
      public void init(ServletConfig servletConfig) throws ServletException {

      }

      @Override
      public ServletConfig getServletConfig() {
      return null;
      }

      @Override
      public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
      \t\tSystem.out.println("实现Servlet接口");
      }

      @Override
      public String getServletInfo() {
      return null;
      }

      @Override
      public void destroy() {

      }
      }

      \n
    4. \n
    \n
  2. \n
\n
    \n
  1. 实现 service()方法,处理请求,响应数据

    \n

    【快捷键 ctrl + o 可以重写方法或实现接口】

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //控制台打印
    System.out.println("Hello Servlet");

    //通过流输出数据到浏览器
    resp.getWriter().write("hello servlet!");
    }
    }
    \n
  2. \n
  3. 设置注解@WebServlet,指定访问路径

    \n
    1
    @WebServlet(name="helloServlet", value = "/ser01") //name可以不设置
    \n
  4. \n
\n
\n

在第二步中,也可以不重写 Service()方法,而是更具体些,重写 doGet()doPost方法;

\n

根据浏览器请求方式来进行不同处理

\n

但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少

\n
\n

Servlet工作流程

urlhttp://localhost:8083/servlet01/ser01含义:

\n
\n
    \n
  • 服务器:http://localhost:8083
  • \n
  • 项目:/servlet01
  • \n
  • 资源:/ser01
  • \n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

Servlet

servlet技术

    \n
  1. Servlet是Java EE规范之一,规范即接口
  2. \n
  3. 是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器
  4. \n
  5. 是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端
  6. \n
  7. Servlet没有 main()方法,由系统自动调用
  8. \n
\n

IDEA创建servlet

\"在这里插入图片描述\"

\n

\"image-20210502163733514\"

\n

\"image-20210502163838549\"

\n

手动实现Servlet程序

    \n
  1. 编写类实现Servlet规范,有3种方式:

    \n
      \n
    1. 继承 HttpServlet

      \n
      1
      2
      3
      public class HelloServlet extends HttpServlet {

      }
      \n
    2. \n
    3. 实现 Servlet接口

      \n
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      package com.example.servlet01;

      import javax.servlet.*;
      import java.io.IOException;

      public class Servlet01 implements Servlet {
      @Override
      public void init(ServletConfig servletConfig) throws ServletException {

      }

      @Override
      public ServletConfig getServletConfig() {
      return null;
      }

      @Override
      public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
      \t\tSystem.out.println("实现Servlet接口");
      }

      @Override
      public String getServletInfo() {
      return null;
      }

      @Override
      public void destroy() {

      }
      }

      \n
    4. \n
    \n
  2. \n
\n
    \n
  1. 实现 service()方法,处理请求,响应数据

    \n

    【快捷键 ctrl + o 可以重写方法或实现接口】

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //控制台打印
    System.out.println("Hello Servlet");

    //通过流输出数据到浏览器
    resp.getWriter().write("hello servlet!");
    }
    }
    \n
  2. \n
  3. 设置注解@WebServlet,指定访问路径

    \n
    1
    @WebServlet(name="helloServlet", value = "/ser01") //name可以不设置
    \n
  4. \n
\n
\n

在第二步中,也可以不重写 Service()方法,而是更具体些,重写 doGet()doPost方法;

\n

根据浏览器请求方式来进行不同处理

\n

但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少

\n
\n

Servlet工作流程

urlhttp://localhost:8083/servlet01/ser01含义:

\n
\n
    \n
  • 服务器:http://localhost:8083
  • \n
  • 项目:/servlet01
  • \n
  • 资源:/ser01
  • \n
\n
\n"},{"title":"2-Servlet生命周期","abbrlink":"e7ba7594","date":"2021-04-12T08:27:24.000Z","description":"Servlet的几个生命周期","_content":"\n\n# Servlet生命周期\n\n\n\n1. 通过调用 `init()`方法创建、初始化服务(系统自动调用,只执行一次)\n2. 调用 `Service()` 方法执行服务\n3. 调用 `destroy()`销毁服务(关闭服务器时系统自动调用,只执行一次)\n\n\n\n#### Servlet工作流程\n\n![image-20210502165959933](https://gitee.com/ajream/images/raw/master/img/2021-05-0217-00-01_image-20210502165959933.png)","source":"_posts/javaEE/2-Servlet生命周期.md","raw":"---\ntitle: 2-Servlet生命周期\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: e7ba7594\ndate: 2021-04-12 16:27:24\ndescription: Servlet的几个生命周期\n---\n\n\n# Servlet生命周期\n\n\n\n1. 通过调用 `init()`方法创建、初始化服务(系统自动调用,只执行一次)\n2. 调用 `Service()` 方法执行服务\n3. 调用 `destroy()`销毁服务(关闭服务器时系统自动调用,只执行一次)\n\n\n\n#### Servlet工作流程\n\n![image-20210502165959933](https://gitee.com/ajream/images/raw/master/img/2021-05-0217-00-01_image-20210502165959933.png)","slug":"javaEE/2-Servlet生命周期","published":1,"updated":"2021-09-10T03:09:15.138Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qd0053akve87tvdadq","content":"

Servlet生命周期

    \n
  1. 通过调用 init()方法创建、初始化服务(系统自动调用,只执行一次)
  2. \n
  3. 调用 Service() 方法执行服务
  4. \n
  5. 调用 destroy()销毁服务(关闭服务器时系统自动调用,只执行一次)
  6. \n
\n

Servlet工作流程

\"image-20210502165959933\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"

Servlet生命周期

    \n
  1. 通过调用 init()方法创建、初始化服务(系统自动调用,只执行一次)
  2. \n
  3. 调用 Service() 方法执行服务
  4. \n
  5. 调用 destroy()销毁服务(关闭服务器时系统自动调用,只执行一次)
  6. \n
\n

Servlet工作流程

\"image-20210502165959933\"

\n"},{"title":"4-请求转发","abbrlink":"1a9b2152","date":"2021-04-14T01:34:29.000Z","description":"Servlet如何处理客户端发来的请求","_content":"\n# 请求转发\n\n1. 可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理)\n2. 是服务端行为\n3. 特点:\n 1. 浏览器地址栏不发生改变\n 2. 实现不同的Servlet数据贡献\n\n\n\n步骤:\n\n1. 分别创建类:Servlet02和Servlet03\n\n 填入以下内容:\n\n ```java\n //类Servlet02\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser02\")\n public class Servlet02 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser02获取到用户名:\" + name);\n \n //跳转到servlet03\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n }\n }\n \n ```\n\n \n\n ```java\n //类Servlet03\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser03\")\n public class servlet03 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser03获取到用户名:\" + name);\n \n }\n }\n \n ```\n\n 在类Servlet02中,跳转语句为:\n\n ```java\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n ```\n\n `ser03`(没有斜线“/”)表示servlet03的注解 “**@WebServlet(\"/ser03\")**”\n\n \n\n > 也可以跳转到 login.jsp页面\n >\n > ```java\n > //跳转到login.jsp\n > req.getRequestDispatcher(\"login.jsp\").forward(req, resp);\n > ```\n\n \n\n2. 启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变\n\n ![image-20210502211542653](https://gitee.com/aJream/images/raw/master/img/2021-05-0221-15-48_image-20210502211542653.png)\n\n \n\n ![image-20210502211627393](https://gitee.com/ajream/images/raw/master/img/2021-05-0221-16-32_image-20210502211627393.png)\n\n\n\n","source":"_posts/javaEE/4-请求转发.md","raw":"---\ntitle: 4-请求转发\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: 1a9b2152\ndate: 2021-04-14 09:34:29\ndescription: Servlet如何处理客户端发来的请求\n---\n\n# 请求转发\n\n1. 可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理)\n2. 是服务端行为\n3. 特点:\n 1. 浏览器地址栏不发生改变\n 2. 实现不同的Servlet数据贡献\n\n\n\n步骤:\n\n1. 分别创建类:Servlet02和Servlet03\n\n 填入以下内容:\n\n ```java\n //类Servlet02\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser02\")\n public class Servlet02 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser02获取到用户名:\" + name);\n \n //跳转到servlet03\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n }\n }\n \n ```\n\n \n\n ```java\n //类Servlet03\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser03\")\n public class servlet03 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser03获取到用户名:\" + name);\n \n }\n }\n \n ```\n\n 在类Servlet02中,跳转语句为:\n\n ```java\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n ```\n\n `ser03`(没有斜线“/”)表示servlet03的注解 “**@WebServlet(\"/ser03\")**”\n\n \n\n > 也可以跳转到 login.jsp页面\n >\n > ```java\n > //跳转到login.jsp\n > req.getRequestDispatcher(\"login.jsp\").forward(req, resp);\n > ```\n\n \n\n2. 启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变\n\n ![image-20210502211542653](https://gitee.com/aJream/images/raw/master/img/2021-05-0221-15-48_image-20210502211542653.png)\n\n \n\n ![image-20210502211627393](https://gitee.com/ajream/images/raw/master/img/2021-05-0221-16-32_image-20210502211627393.png)\n\n\n\n","slug":"javaEE/4-请求转发","published":1,"updated":"2021-09-10T03:10:21.610Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qe0056akvea9ks9ha1","content":"

请求转发

    \n
  1. 可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理)
  2. \n
  3. 是服务端行为
  4. \n
  5. 特点:
      \n
    1. 浏览器地址栏不发生改变
    2. \n
    3. 实现不同的Servlet数据贡献
    4. \n
    \n
  6. \n
\n

步骤:

\n
    \n
  1. 分别创建类:Servlet02和Servlet03

    \n

    填入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //类Servlet02

    package com.xxxx.servlet02;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    @WebServlet("/ser02")
    public class Servlet02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String name = req.getParameter("name");
    System.out.println("ser02获取到用户名:" + name);

    //跳转到servlet03
    req.getRequestDispatcher("ser03").forward(req, resp);
    }
    }

    \n
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//类Servlet03

package com.xxxx.servlet02;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/ser03")
public class servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
System.out.println("ser03获取到用户名:" + name);

}
}

\n

在类Servlet02中,跳转语句为:

\n
1
req.getRequestDispatcher("ser03").forward(req, resp);
\n

ser03(没有斜线“/”)表示servlet03的注解 “@WebServlet(“/ser03”)

\n
\n

也可以跳转到 login.jsp页面

\n
1
2
//跳转到login.jsp
req.getRequestDispatcher("login.jsp").forward(req, resp);
\n
\n
    \n
  1. 启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变

    \n

    \"image-20210502211542653\"

    \n
  2. \n
\n

\"image-20210502211627393\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

请求转发

    \n
  1. 可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理)
  2. \n
  3. 是服务端行为
  4. \n
  5. 特点:
      \n
    1. 浏览器地址栏不发生改变
    2. \n
    3. 实现不同的Servlet数据贡献
    4. \n
    \n
  6. \n
\n

步骤:

\n
    \n
  1. 分别创建类:Servlet02和Servlet03

    \n

    填入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //类Servlet02

    package com.xxxx.servlet02;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    @WebServlet("/ser02")
    public class Servlet02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String name = req.getParameter("name");
    System.out.println("ser02获取到用户名:" + name);

    //跳转到servlet03
    req.getRequestDispatcher("ser03").forward(req, resp);
    }
    }

    \n
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//类Servlet03

package com.xxxx.servlet02;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/ser03")
public class servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
System.out.println("ser03获取到用户名:" + name);

}
}

\n

在类Servlet02中,跳转语句为:

\n
1
req.getRequestDispatcher("ser03").forward(req, resp);
\n

ser03(没有斜线“/”)表示servlet03的注解 “@WebServlet(“/ser03”)

\n
\n

也可以跳转到 login.jsp页面

\n
1
2
//跳转到login.jsp
req.getRequestDispatcher("login.jsp").forward(req, resp);
\n
\n
    \n
  1. 启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变

    \n

    \"image-20210502211542653\"

    \n
  2. \n
\n

\"image-20210502211627393\"

\n"},{"title":"IDEA配置Tomcat","abbrlink":"1a57c6b6","date":"2021-06-10T06:51:17.000Z","description":"关于如何在idea中配置Tomcat服务器的具体操作","_content":"\n\n\n三个步骤\n\n1. \n\n![image-20210502152308870](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-23-11_image-20210502152308870.png)\n\n2. \n\n![image-20210502152803463](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-28-04_image-20210502152803463.png)\n\n3. \n\n![image-20210502152955436](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-29-56_image-20210502152955436.png)\n\n\n\n","source":"_posts/javaEE/IDEA配置Tomcat.md","raw":"---\ntitle: IDEA配置Tomcat\ntags:\n - Tomcat\ncategories:\n - - java\n - javaEE\nabbrlink: 1a57c6b6\ndate: 2021-06-10 14:51:17\ndescription: 关于如何在idea中配置Tomcat服务器的具体操作\n---\n\n\n\n三个步骤\n\n1. \n\n![image-20210502152308870](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-23-11_image-20210502152308870.png)\n\n2. \n\n![image-20210502152803463](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-28-04_image-20210502152803463.png)\n\n3. \n\n![image-20210502152955436](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-29-56_image-20210502152955436.png)\n\n\n\n","slug":"javaEE/IDEA配置Tomcat","published":1,"updated":"2021-09-10T03:11:01.852Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qf0058akve50qfda62","content":"

三个步骤

\n
    \n
  1. \"image-20210502152308870\"

    \n
  2. \n
  3. \n
\n

\"image-20210502152803463\"

\n
    \n
  1. \"image-20210502152955436\"

    \n
  2. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

三个步骤

\n
    \n
  1. \"image-20210502152308870\"

    \n
  2. \n
  3. \n
\n

\"image-20210502152803463\"

\n
    \n
  1. \"image-20210502152955436\"

    \n
  2. \n
\n"},{"title":"3-ServletRequest对象","abbrlink":"4609d38b","date":"2021-04-13T10:27:34.000Z","description":"Servlet的Request对象使用","_content":"\n\n\n\n\n\n# HttpServletRequest对象\n\n\n\n该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等\n\n\n\nHttpServletRequest是ServletRequest的唯一的子**接口**\n\n\n\n## 接收请求\n\n### 常用方法\n\n```java\n//完整url路径\nString url = req.getRequestURL().toString();\n\n//部分路径\nString uri = req.getRequestURI();\n\n//获取请求的参数(如果有的话,即路径中`?`后面的字符串)\nString query = req.getQueryString();\n\n//请求方式get、post\nString method = req.getMethod();\n\n//请求协议(http/1/1)\nString protocol = req.getProtocol();\n\n//获取项目站点名\nString webapp = req.getContextPath();\n\n```\n\n请求为【`localhost:8083/sr02/servlet02`】时输出:\n\n![image-20210502192535571](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-25-40_image-20210502192535571.png)\n\n请求为【`localhost:8083/sr02/servlet02?name=xiaod&passwd=123456`】时:\n\n![image-20210502192917681](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-29-20_image-20210502192917681.png)\n\n\n\n**详细参数获取(重点)**\n\n```java\n//获取指定参数,返回字符串\nString name = req.getParameter(\"name\"); //用户名\nString passwd = req.getParameter(\"passwd\"); //密码\n```\n\n![image-20210502193840780](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-38-47_image-20210502193840780.png)\n\n\n\n获取多个同名参数(适用于复选框)\n\n```java\n//多个参数名称相同,用数组收集\n//如:参数为 name=xiaod&passwd=123456&hobby=sing&hobby=dance\nString[] hobbies = req.getParameterValues(\"hobby\"); \n```\n\n\n\n\n\n## 中文乱码问题\n\ntomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决\n\n\n\n使用**post请求**提交表单时,若含有中文,获取到的是乱码\n\n**验证步骤**\n\n1. 在webapp下创建login.jsp文件\n\n \"image-20210502201756825\"\n\n2. 在login.jsp下粘贴以下代码\n\n ```jsp\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n 登录\n
\n 账户名:
\n 密码:
\n \n
\n \n \n \n \n \n \n ```\n\n > 需要注意,form标签的属性 `action`为**注解@WebServlet(\"/servlet02\")**中的 `servlet02`(不要有斜线),这样就与这个处理类关联起来了\n\n3. 运行启动\n\n4. 浏览器访问\n\n ![image-20210502202959582](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-30-03_image-20210502202959582.png)\n\n5. 点击登录提交给 `Servlet01`类(含有注解 `@WebServlet(\"/servlet02\")` )处理\n\n 查看获取到的数据:\n\n ![image-20210502203233059](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-32-36_image-20210502203233059.png)\n\n","source":"_posts/javaEE/3-ServletRequest对象.md","raw":"---\ntitle: 3-ServletRequest对象\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: 4609d38b\ndate: 2021-04-13 18:27:34\ndescription: Servlet的Request对象使用\n---\n\n\n\n\n\n\n# HttpServletRequest对象\n\n\n\n该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等\n\n\n\nHttpServletRequest是ServletRequest的唯一的子**接口**\n\n\n\n## 接收请求\n\n### 常用方法\n\n```java\n//完整url路径\nString url = req.getRequestURL().toString();\n\n//部分路径\nString uri = req.getRequestURI();\n\n//获取请求的参数(如果有的话,即路径中`?`后面的字符串)\nString query = req.getQueryString();\n\n//请求方式get、post\nString method = req.getMethod();\n\n//请求协议(http/1/1)\nString protocol = req.getProtocol();\n\n//获取项目站点名\nString webapp = req.getContextPath();\n\n```\n\n请求为【`localhost:8083/sr02/servlet02`】时输出:\n\n![image-20210502192535571](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-25-40_image-20210502192535571.png)\n\n请求为【`localhost:8083/sr02/servlet02?name=xiaod&passwd=123456`】时:\n\n![image-20210502192917681](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-29-20_image-20210502192917681.png)\n\n\n\n**详细参数获取(重点)**\n\n```java\n//获取指定参数,返回字符串\nString name = req.getParameter(\"name\"); //用户名\nString passwd = req.getParameter(\"passwd\"); //密码\n```\n\n![image-20210502193840780](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-38-47_image-20210502193840780.png)\n\n\n\n获取多个同名参数(适用于复选框)\n\n```java\n//多个参数名称相同,用数组收集\n//如:参数为 name=xiaod&passwd=123456&hobby=sing&hobby=dance\nString[] hobbies = req.getParameterValues(\"hobby\"); \n```\n\n\n\n\n\n## 中文乱码问题\n\ntomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决\n\n\n\n使用**post请求**提交表单时,若含有中文,获取到的是乱码\n\n**验证步骤**\n\n1. 在webapp下创建login.jsp文件\n\n \"image-20210502201756825\"\n\n2. 在login.jsp下粘贴以下代码\n\n ```jsp\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n 登录\n
\n 账户名:
\n 密码:
\n \n
\n \n \n \n \n \n \n ```\n\n > 需要注意,form标签的属性 `action`为**注解@WebServlet(\"/servlet02\")**中的 `servlet02`(不要有斜线),这样就与这个处理类关联起来了\n\n3. 运行启动\n\n4. 浏览器访问\n\n ![image-20210502202959582](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-30-03_image-20210502202959582.png)\n\n5. 点击登录提交给 `Servlet01`类(含有注解 `@WebServlet(\"/servlet02\")` )处理\n\n 查看获取到的数据:\n\n ![image-20210502203233059](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-32-36_image-20210502203233059.png)\n\n","slug":"javaEE/3-ServletRequest对象","published":1,"updated":"2021-09-10T03:10:07.722Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qg005aakveh3vd1y50","content":"

HttpServletRequest对象

该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等

\n

HttpServletRequest是ServletRequest的唯一的子接口

\n

接收请求

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//完整url路径
String url = req.getRequestURL().toString();

//部分路径
String uri = req.getRequestURI();

//获取请求的参数(如果有的话,即路径中`?`后面的字符串)
String query = req.getQueryString();

//请求方式get、post
String method = req.getMethod();

//请求协议(http/1/1)
String protocol = req.getProtocol();

//获取项目站点名
String webapp = req.getContextPath();

\n

请求为【localhost:8083/sr02/servlet02】时输出:

\n

\"image-20210502192535571\"

\n

请求为【localhost:8083/sr02/servlet02?name=xiaod&passwd=123456】时:

\n

\"image-20210502192917681\"

\n

详细参数获取(重点)

\n
1
2
3
//获取指定参数,返回字符串
String name = req.getParameter("name"); //用户名
String passwd = req.getParameter("passwd"); //密码
\n

\"image-20210502193840780\"

\n

获取多个同名参数(适用于复选框)

\n
1
2
3
//多个参数名称相同,用数组收集
//如:参数为 name=xiaod&passwd=123456&hobby=sing&hobby=dance
String[] hobbies = req.getParameterValues("hobby");
\n

中文乱码问题

tomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决

\n

使用post请求提交表单时,若含有中文,获取到的是乱码

\n

验证步骤

\n
    \n
  1. 在webapp下创建login.jsp文件

    \n

    \"image-20210502201756825\"

    \n
  2. \n
  3. 在login.jsp下粘贴以下代码

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>登录</title>
    <form method="post" action="servlet02">
    账户名:<input type="text" name="name"> <br>
    密码:<input type="password" name="passwd"> <br>
    <button>登录</button> <!--button默认为提交按钮-->
    </form>
    </head>
    <body>

    </body>
    </html>

    \n
    \n

    需要注意,form标签的属性 action注解@WebServlet(“/servlet02”)中的 servlet02(不要有斜线),这样就与这个处理类关联起来了

    \n
    \n
  4. \n
  5. 运行启动

    \n
  6. \n
  7. 浏览器访问

    \n

    \"image-20210502202959582\"

    \n
  8. \n
  9. 点击登录提交给 Servlet01类(含有注解 @WebServlet("/servlet02") )处理

    \n

    查看获取到的数据:

    \n

    \"image-20210502203233059\"

    \n
  10. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

HttpServletRequest对象

该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等

\n

HttpServletRequest是ServletRequest的唯一的子接口

\n

接收请求

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//完整url路径
String url = req.getRequestURL().toString();

//部分路径
String uri = req.getRequestURI();

//获取请求的参数(如果有的话,即路径中`?`后面的字符串)
String query = req.getQueryString();

//请求方式get、post
String method = req.getMethod();

//请求协议(http/1/1)
String protocol = req.getProtocol();

//获取项目站点名
String webapp = req.getContextPath();

\n

请求为【localhost:8083/sr02/servlet02】时输出:

\n

\"image-20210502192535571\"

\n

请求为【localhost:8083/sr02/servlet02?name=xiaod&passwd=123456】时:

\n

\"image-20210502192917681\"

\n

详细参数获取(重点)

\n
1
2
3
//获取指定参数,返回字符串
String name = req.getParameter("name"); //用户名
String passwd = req.getParameter("passwd"); //密码
\n

\"image-20210502193840780\"

\n

获取多个同名参数(适用于复选框)

\n
1
2
3
//多个参数名称相同,用数组收集
//如:参数为 name=xiaod&passwd=123456&hobby=sing&hobby=dance
String[] hobbies = req.getParameterValues("hobby");
\n

中文乱码问题

tomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决

\n

使用post请求提交表单时,若含有中文,获取到的是乱码

\n

验证步骤

\n
    \n
  1. 在webapp下创建login.jsp文件

    \n

    \"image-20210502201756825\"

    \n
  2. \n
  3. 在login.jsp下粘贴以下代码

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>登录</title>
    <form method="post" action="servlet02">
    账户名:<input type="text" name="name"> <br>
    密码:<input type="password" name="passwd"> <br>
    <button>登录</button> <!--button默认为提交按钮-->
    </form>
    </head>
    <body>

    </body>
    </html>

    \n
    \n

    需要注意,form标签的属性 action注解@WebServlet(“/servlet02”)中的 servlet02(不要有斜线),这样就与这个处理类关联起来了

    \n
    \n
  4. \n
  5. 运行启动

    \n
  6. \n
  7. 浏览器访问

    \n

    \"image-20210502202959582\"

    \n
  8. \n
  9. 点击登录提交给 Servlet01类(含有注解 @WebServlet("/servlet02") )处理

    \n

    查看获取到的数据:

    \n

    \"image-20210502203233059\"

    \n
  10. \n
\n"},{"title":"1-类与对象","abbrlink":"52b44ec7","date":"2021-02-05T05:12:19.000Z","description":"关于Java的类的基本知识,例如类的属性、方法(传参及调用)、继承、this关键字等","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n\n# 类与对象\n\n类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;\n\n用代码创建一个鸟类:\n\n```java\npublic class Bird{\n String name;\n String feathers_color;\n public void fly(){\n System.out.println(name + \"可以飞\")\n }\n}\n```\n\n对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;\n\n用代码来创建对象:\n\n```java\nBird bird1 = new Bird();\nBird bird2 = new Bird();\n```\n\n## 引用与指向\n\n在下面创建的这一对象中\n\n```java\nBird bird1 = new Bird();\n```\n\n`bird1`存储的是对象的地址,因此`bird1`是对象的**引用**;这一地址**指向**了对象本身;\n\n\"image-20210123153305198\"\n\n- 多个引用指向同一对象\n\n```java\nBird b1 = new Bird();\nBird b2 = b1;\nBird b3 = b1;\n```\n\n\"image-20210123154400728\"\n\n- 多个引用指向多个(不同)对象\n\n```java\nBird b1 = new Bird();\nBird b2 = new Bird();\nBird b3 = new Bird();\n```\n\n\"image-20210123154635298\"\n\n## 继承\n\n可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:\n\n```java\npublic class ClassName2 extends ClassName1{\n\n}\n```\n\n例如:\n\n首先创建父类Item\n\n```java\npublic class Item {\n String name;\n int price;\n}\n```\n\n创建子类Weapon继承Item\n\n```java\npublic class Weapon extends Item{\n int damage; //攻击力\n \n public static void main(String[] args) {\n Weapon infinityEdge = new Weapon();\n infinityEdge.damage = 65; //damage属性在类Weapon中新设计的\n \n infinityEdge.name = \"无尽之刃\";//name属性,是从Item中继承来的,就不需要重复设计了\n infinityEdge.price = 3600;\n \n }\n \n}\n```\n\n## 方法重载\n\n方法的重载指的是**方法名一样**,但是参数类型或数量不一样\n\n```java\npublic class ADHero extends Hero {\n //无参数\n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n \t//1个参数\n public void attack(Hero h1) {\n System.out.println(name + \"对\" + h1.name + \"进行了一次攻击 \");\n }\n \t\n //2个参数\n public void attack(Hero h1, Hero h2) {\n System.out.println(name + \"同时对\" + h1.name + \"和\" + h2.name + \"进行了攻击 \");\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n }\n \n}\n```\n\n可变数量的参数\n\n采用可变数量的参数 **只需要设计一个方法**:\n`public void attack(Hero... heros)`\n即可代表上述所有的方法了,此时 `heros`被当作一个**数组**,在方法里,使用操作数组的方式处理参数heros即可\n\n```java\npublic class ADHero extends Hero {\n \n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n // 可变数量的参数\n public void attack(Hero... heros) {\n for (int i = 0; i < heros.length; i++) {\n System.out.println(name + \" 攻击了 \" + heros[i].name);\n \n }\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n \n }\n \n}\n```\n\n## 构造方法\n\n构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法\n\n```java\npublic class Hero {\n \n String name;\n \n float hp;\n \n float armor;\n \n int moveSpeed;\n \n // 方法名和类名一样(包括大小写)\n // 没有返回类型\n public Hero() {\n System.out.println(\"实例化一个对象的时候,必然调用构造方法\");\n }\n \n public static void main(String[] args) {\n //实例化一个对象的时候,必然调用构造方法\n Hero h = new Hero();\n }\n \n}\n\n//输出:实例化一个对象的时候,必然调用构造方法\n```\n\n构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同\n\n## this\n\n`this` 代表**当前对象**,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有\n\n```java\npublic class Hero {\n \n String name; //姓名\n \n float hp; //血量\n \n float armor; //护甲\n \n int moveSpeed; //移动速度\n \n //打印内存中的虚拟地址\n public void showAddressInMemory(){\n System.out.println(\"打印this看到的虚拟地址:\"+this);\n }\n \n public static void main(String[] args) {\n //---------------------------------------------\n Hero garen = new Hero();\n garen.name = \"aaa\";\n System.out.println(\"打印对象看到的虚拟地址:\"+garen);\n garen.showAddressInMemory();\n \n //-------------------------------------------\n Hero teemo = new Hero();\n teemo.name = \"bbb\";\n System.out.println(\"打印对象看到的虚拟地址:\"+teemo);\n teemo.showAddressInMemory();\n } \n \n}\n\n//输出:\n/*\n打印对象看到的虚拟地址:Hero@2a139a55\n打印this看到的虚拟地址:Hero@2a139a55\n打印对象看到的虚拟地址:Hero@15db9742\n打印this看到的虚拟地址:Hero@15db9742\n*/\n```\n\n通过`this` 可以访问对象的属性、方法\n\n通过`this()`可以调用其他的构造方法\n\n```java\npublic class Hero {\n\n String name; // 姓名\n\n float hp; // 血量\n\n float armor; // 护甲\n\n int moveSpeed; // 移动速度\n\n // 带一个参数的构造方法\n public Hero(String name) {\n System.out.println(\"一个参数的构造方法\");\n this.name = name;\n }\n\n // 带两个参数的构造方法\n public Hero(String name, float hp) {\n this(name); \n System.out.println(\"两个参数的构造方法\");\n this.hp = hp;\n }\n\n public static void main(String[] args) {\n Hero teemo = new Hero(\"aaaa\", 383); //两个参数\n\n System.out.println(teemo.name);\n\n }\n\n}\n\n/*输出:\n\n一个参数的构造方法\n两个参数的构造方法\naaaa\n*/\n```\n\n## 传参\n\n- 基本类型传参与引用类型传参\n\n 基本类型传参,不多说\n\n 引用类型传参,即传入参数为引用类型\n\n- `=`的含义\n\n - 如果变量是基本类型,则 `=`表示赋值\n\n - 如果变量是引用类型,`=` 表示指向的意思,比如\n\n ```java\n Hero h = new Hero(); //引用h,指向一个Hero对象\n ```\n\n- 引用类型传参\n\n 形参与实参都指向同一对象\n\n ```java\n public class Hero {\n \n String name; \n \n float hp;\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@2a139a55\n */\n ```\n\n 用图表示如下![image-20210123184909979](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210123184909979.png)\n\n 但如果形参在内部指向了新的对象,实参是不会跟着改变的\n\n ```java\n public class Hero {\n \n String name; // 姓名\n \n float hp; // 血量\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n hero = new Hero(\"hero\", 100); //形参指向了新的对象\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@15db9742\n */\n ```\n\n 用图表示如下![image-20210123185224792](https://gitee.com/ajream/images/raw/master/img/image-20210123185224792.png)\n\n\n\n## 类属性(又叫静态属性)\n\n当一个属性被**`static`**修饰的时候,就叫做**类属性**,又叫做**静态属性**\n当一个属性被声明成类属性,那么**所有的对象,都共享一个值**\n\n**对象属性:** 又叫实例属性,非静态属性\n\n> 与对象属性对比:\n> 不同对象的 对象属性 的值都可能不一样。\n\n- 访问类属性:\n\n ```java\n 类.类属性\n ```\n\n ```java\n //访问对象属性:\n 对象.对象属性\n ```\n\n- 类属性与对象属性的选择\n\n - 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是**跟着对象走的**,每个对象的name都是不同的\n - 如果一个属性,**所有的英雄都共享**,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性\n\n\n\n## 类方法(静态方法)\n\n**类方法:** 又叫做静态方法,即被`static`修饰的方法\n\n**对象方法:** 又叫实例方法,非静态方法\n\n> 区别:\n>\n> 调用一个对象方法,必须**建立在有一个对象**的前提的基础上\n> 调用类方法,**不需要对象**的存在,直接就访问\n\n- 调用类方法\n\n ```java\n 类.类方法\n ```\n\n ```java\n //访问对象方法\n 对象.对象方法\n ```\n\n- 什么时候用类方法\n\n 如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如:\n\n ```java\n public static void printGameDuration(){\n \tSystem.out.println(\"已经玩了50秒\");\n }\n ```\n\n 反之,如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法;\n\n 注意:类方法中不能直接调用对象方法\n\n---\n\n[回到开头](#1-类与对象)\n\n","source":"_posts/javaSE/1-类与对象.md","raw":"---\ntitle: 1-类与对象\ntags:\n - javaSE\ncategories:\n - java\n - javaSE\nabbrlink: 52b44ec7\ndate: 2021-02-05 13:12:19\ndescription: 关于Java的类的基本知识,例如类的属性、方法(传参及调用)、继承、this关键字等\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n\n# 类与对象\n\n类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;\n\n用代码创建一个鸟类:\n\n```java\npublic class Bird{\n String name;\n String feathers_color;\n public void fly(){\n System.out.println(name + \"可以飞\")\n }\n}\n```\n\n对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;\n\n用代码来创建对象:\n\n```java\nBird bird1 = new Bird();\nBird bird2 = new Bird();\n```\n\n## 引用与指向\n\n在下面创建的这一对象中\n\n```java\nBird bird1 = new Bird();\n```\n\n`bird1`存储的是对象的地址,因此`bird1`是对象的**引用**;这一地址**指向**了对象本身;\n\n\"image-20210123153305198\"\n\n- 多个引用指向同一对象\n\n```java\nBird b1 = new Bird();\nBird b2 = b1;\nBird b3 = b1;\n```\n\n\"image-20210123154400728\"\n\n- 多个引用指向多个(不同)对象\n\n```java\nBird b1 = new Bird();\nBird b2 = new Bird();\nBird b3 = new Bird();\n```\n\n\"image-20210123154635298\"\n\n## 继承\n\n可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:\n\n```java\npublic class ClassName2 extends ClassName1{\n\n}\n```\n\n例如:\n\n首先创建父类Item\n\n```java\npublic class Item {\n String name;\n int price;\n}\n```\n\n创建子类Weapon继承Item\n\n```java\npublic class Weapon extends Item{\n int damage; //攻击力\n \n public static void main(String[] args) {\n Weapon infinityEdge = new Weapon();\n infinityEdge.damage = 65; //damage属性在类Weapon中新设计的\n \n infinityEdge.name = \"无尽之刃\";//name属性,是从Item中继承来的,就不需要重复设计了\n infinityEdge.price = 3600;\n \n }\n \n}\n```\n\n## 方法重载\n\n方法的重载指的是**方法名一样**,但是参数类型或数量不一样\n\n```java\npublic class ADHero extends Hero {\n //无参数\n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n \t//1个参数\n public void attack(Hero h1) {\n System.out.println(name + \"对\" + h1.name + \"进行了一次攻击 \");\n }\n \t\n //2个参数\n public void attack(Hero h1, Hero h2) {\n System.out.println(name + \"同时对\" + h1.name + \"和\" + h2.name + \"进行了攻击 \");\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n }\n \n}\n```\n\n可变数量的参数\n\n采用可变数量的参数 **只需要设计一个方法**:\n`public void attack(Hero... heros)`\n即可代表上述所有的方法了,此时 `heros`被当作一个**数组**,在方法里,使用操作数组的方式处理参数heros即可\n\n```java\npublic class ADHero extends Hero {\n \n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n // 可变数量的参数\n public void attack(Hero... heros) {\n for (int i = 0; i < heros.length; i++) {\n System.out.println(name + \" 攻击了 \" + heros[i].name);\n \n }\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n \n }\n \n}\n```\n\n## 构造方法\n\n构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法\n\n```java\npublic class Hero {\n \n String name;\n \n float hp;\n \n float armor;\n \n int moveSpeed;\n \n // 方法名和类名一样(包括大小写)\n // 没有返回类型\n public Hero() {\n System.out.println(\"实例化一个对象的时候,必然调用构造方法\");\n }\n \n public static void main(String[] args) {\n //实例化一个对象的时候,必然调用构造方法\n Hero h = new Hero();\n }\n \n}\n\n//输出:实例化一个对象的时候,必然调用构造方法\n```\n\n构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同\n\n## this\n\n`this` 代表**当前对象**,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有\n\n```java\npublic class Hero {\n \n String name; //姓名\n \n float hp; //血量\n \n float armor; //护甲\n \n int moveSpeed; //移动速度\n \n //打印内存中的虚拟地址\n public void showAddressInMemory(){\n System.out.println(\"打印this看到的虚拟地址:\"+this);\n }\n \n public static void main(String[] args) {\n //---------------------------------------------\n Hero garen = new Hero();\n garen.name = \"aaa\";\n System.out.println(\"打印对象看到的虚拟地址:\"+garen);\n garen.showAddressInMemory();\n \n //-------------------------------------------\n Hero teemo = new Hero();\n teemo.name = \"bbb\";\n System.out.println(\"打印对象看到的虚拟地址:\"+teemo);\n teemo.showAddressInMemory();\n } \n \n}\n\n//输出:\n/*\n打印对象看到的虚拟地址:Hero@2a139a55\n打印this看到的虚拟地址:Hero@2a139a55\n打印对象看到的虚拟地址:Hero@15db9742\n打印this看到的虚拟地址:Hero@15db9742\n*/\n```\n\n通过`this` 可以访问对象的属性、方法\n\n通过`this()`可以调用其他的构造方法\n\n```java\npublic class Hero {\n\n String name; // 姓名\n\n float hp; // 血量\n\n float armor; // 护甲\n\n int moveSpeed; // 移动速度\n\n // 带一个参数的构造方法\n public Hero(String name) {\n System.out.println(\"一个参数的构造方法\");\n this.name = name;\n }\n\n // 带两个参数的构造方法\n public Hero(String name, float hp) {\n this(name); \n System.out.println(\"两个参数的构造方法\");\n this.hp = hp;\n }\n\n public static void main(String[] args) {\n Hero teemo = new Hero(\"aaaa\", 383); //两个参数\n\n System.out.println(teemo.name);\n\n }\n\n}\n\n/*输出:\n\n一个参数的构造方法\n两个参数的构造方法\naaaa\n*/\n```\n\n## 传参\n\n- 基本类型传参与引用类型传参\n\n 基本类型传参,不多说\n\n 引用类型传参,即传入参数为引用类型\n\n- `=`的含义\n\n - 如果变量是基本类型,则 `=`表示赋值\n\n - 如果变量是引用类型,`=` 表示指向的意思,比如\n\n ```java\n Hero h = new Hero(); //引用h,指向一个Hero对象\n ```\n\n- 引用类型传参\n\n 形参与实参都指向同一对象\n\n ```java\n public class Hero {\n \n String name; \n \n float hp;\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@2a139a55\n */\n ```\n\n 用图表示如下![image-20210123184909979](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210123184909979.png)\n\n 但如果形参在内部指向了新的对象,实参是不会跟着改变的\n\n ```java\n public class Hero {\n \n String name; // 姓名\n \n float hp; // 血量\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n hero = new Hero(\"hero\", 100); //形参指向了新的对象\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@15db9742\n */\n ```\n\n 用图表示如下![image-20210123185224792](https://gitee.com/ajream/images/raw/master/img/image-20210123185224792.png)\n\n\n\n## 类属性(又叫静态属性)\n\n当一个属性被**`static`**修饰的时候,就叫做**类属性**,又叫做**静态属性**\n当一个属性被声明成类属性,那么**所有的对象,都共享一个值**\n\n**对象属性:** 又叫实例属性,非静态属性\n\n> 与对象属性对比:\n> 不同对象的 对象属性 的值都可能不一样。\n\n- 访问类属性:\n\n ```java\n 类.类属性\n ```\n\n ```java\n //访问对象属性:\n 对象.对象属性\n ```\n\n- 类属性与对象属性的选择\n\n - 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是**跟着对象走的**,每个对象的name都是不同的\n - 如果一个属性,**所有的英雄都共享**,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性\n\n\n\n## 类方法(静态方法)\n\n**类方法:** 又叫做静态方法,即被`static`修饰的方法\n\n**对象方法:** 又叫实例方法,非静态方法\n\n> 区别:\n>\n> 调用一个对象方法,必须**建立在有一个对象**的前提的基础上\n> 调用类方法,**不需要对象**的存在,直接就访问\n\n- 调用类方法\n\n ```java\n 类.类方法\n ```\n\n ```java\n //访问对象方法\n 对象.对象方法\n ```\n\n- 什么时候用类方法\n\n 如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如:\n\n ```java\n public static void printGameDuration(){\n \tSystem.out.println(\"已经玩了50秒\");\n }\n ```\n\n 反之,如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法;\n\n 注意:类方法中不能直接调用对象方法\n\n---\n\n[回到开头](#1-类与对象)\n\n","slug":"javaSE/1-类与对象","published":1,"updated":"2021-09-10T03:05:35.295Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qj005dakve37dy5lne","content":"

类与对象

类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;

\n

用代码创建一个鸟类:

\n
1
2
3
4
5
6
7
public class Bird{
String name;
String feathers_color;
public void fly(){
System.out.println(name + "可以飞")
}
}
\n

对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;

\n

用代码来创建对象:

\n
1
2
Bird bird1 = new Bird();
Bird bird2 = new Bird();
\n

引用与指向

在下面创建的这一对象中

\n
1
Bird bird1 = new Bird();
\n

bird1存储的是对象的地址,因此bird1是对象的引用;这一地址指向了对象本身;

\n

\"image-20210123153305198\"

\n
    \n
  • 多个引用指向同一对象
  • \n
\n
1
2
3
Bird b1 = new Bird();
Bird b2 = b1;
Bird b3 = b1;
\n

\"image-20210123154400728\"

\n
    \n
  • 多个引用指向多个(不同)对象
  • \n
\n
1
2
3
Bird b1 = new Bird();
Bird b2 = new Bird();
Bird b3 = new Bird();
\n

\"image-20210123154635298\"

\n

继承

可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:

\n
1
2
3
public class ClassName2 extends ClassName1{

}
\n

例如:

\n

首先创建父类Item

\n
1
2
3
4
public class Item {
String name;
int price;
}
\n

创建子类Weapon继承Item

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Weapon extends Item{
int damage; //攻击力

public static void main(String[] args) {
Weapon infinityEdge = new Weapon();
infinityEdge.damage = 65; //damage属性在类Weapon中新设计的

infinityEdge.name = "无尽之刃";//name属性,是从Item中继承来的,就不需要重复设计了
infinityEdge.price = 3600;

}

}
\n

方法重载

方法的重载指的是方法名一样,但是参数类型或数量不一样

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ADHero extends Hero {
//无参数
public void attack() {
System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
}

\t//1个参数
public void attack(Hero h1) {
System.out.println(name + "对" + h1.name + "进行了一次攻击 ");
}
\t
//2个参数
public void attack(Hero h1, Hero h2) {
System.out.println(name + "同时对" + h1.name + "和" + h2.name + "进行了攻击 ");
}

public static void main(String[] args) {
ADHero bh = new ADHero();
bh.name = "赏金猎人";

Hero h1 = new Hero();
h1.name = "盖伦";
Hero h2 = new Hero();
h2.name = "提莫";

bh.attack(h1);
bh.attack(h1, h2);
}

}
\n

可变数量的参数

\n

采用可变数量的参数 只需要设计一个方法
public void attack(Hero... heros)
即可代表上述所有的方法了,此时 heros被当作一个数组,在方法里,使用操作数组的方式处理参数heros即可

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ADHero extends Hero {

public void attack() {
System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
}

// 可变数量的参数
public void attack(Hero... heros) {
for (int i = 0; i < heros.length; i++) {
System.out.println(name + " 攻击了 " + heros[i].name);

}
}

public static void main(String[] args) {
ADHero bh = new ADHero();
bh.name = "赏金猎人";

Hero h1 = new Hero();
h1.name = "盖伦";
Hero h2 = new Hero();
h2.name = "提莫";

bh.attack(h1);
bh.attack(h1, h2);

}

}
\n

构造方法

构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Hero {

String name;

float hp;

float armor;

int moveSpeed;

// 方法名和类名一样(包括大小写)
// 没有返回类型
public Hero() {
System.out.println("实例化一个对象的时候,必然调用构造方法");
}

public static void main(String[] args) {
//实例化一个对象的时候,必然调用构造方法
Hero h = new Hero();
}

}

//输出:实例化一个对象的时候,必然调用构造方法
\n

构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同

\n

this

this 代表当前对象,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Hero {

String name; //姓名

float hp; //血量

float armor; //护甲

int moveSpeed; //移动速度

//打印内存中的虚拟地址
public void showAddressInMemory(){
System.out.println("打印this看到的虚拟地址:"+this);
}

public static void main(String[] args) {
//---------------------------------------------
Hero garen = new Hero();
garen.name = "aaa";
System.out.println("打印对象看到的虚拟地址:"+garen);
garen.showAddressInMemory();

//-------------------------------------------
Hero teemo = new Hero();
teemo.name = "bbb";
System.out.println("打印对象看到的虚拟地址:"+teemo);
teemo.showAddressInMemory();
}

}

//输出:
/*
打印对象看到的虚拟地址:Hero@2a139a55
打印this看到的虚拟地址:Hero@2a139a55
打印对象看到的虚拟地址:Hero@15db9742
打印this看到的虚拟地址:Hero@15db9742
*/
\n

通过this 可以访问对象的属性、方法

\n

通过this()可以调用其他的构造方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Hero {

String name; // 姓名

float hp; // 血量

float armor; // 护甲

int moveSpeed; // 移动速度

// 带一个参数的构造方法
public Hero(String name) {
System.out.println("一个参数的构造方法");
this.name = name;
}

// 带两个参数的构造方法
public Hero(String name, float hp) {
this(name);
System.out.println("两个参数的构造方法");
this.hp = hp;
}

public static void main(String[] args) {
Hero teemo = new Hero("aaaa", 383); //两个参数

System.out.println(teemo.name);

}

}

/*输出:

一个参数的构造方法
两个参数的构造方法
aaaa
*/
\n

传参

    \n
  • 基本类型传参与引用类型传参

    \n

    基本类型传参,不多说

    \n

    引用类型传参,即传入参数为引用类型

    \n
  • \n
  • =的含义

    \n
      \n
    • 如果变量是基本类型,则 =表示赋值

      \n
    • \n
    • 如果变量是引用类型,= 表示指向的意思,比如

      \n
      1
      Hero h = new Hero();  //引用h,指向一个Hero对象
      \n
    • \n
    \n
  • \n
  • 引用类型传参

    \n

    形参与实参都指向同一对象

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Hero {

    String name;

    float hp;

    public Hero(String name, float hp) {
    this.name = name;
    this.hp = hp;
    }

    public void attack(Hero hero) {
    System.out.println("hero:"+hero);
    }

    public static void main(String[] args) {
    Hero teemo = new Hero("teemo", 383);
    Hero garen = new Hero("garen", 616);
    garen.attack(teemo);
    System.out.println("teemo:"+teemo);

    }

    }

    /**输出:
    hero:Hero@2a139a55
    teemo:Hero@2a139a55
    */
    \n

    用图表示如下\"image-20210123184909979\"

    \n

    但如果形参在内部指向了新的对象,实参是不会跟着改变的

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Hero {

    String name; // 姓名

    float hp; // 血量

    public Hero(String name, float hp) {
    this.name = name;
    this.hp = hp;
    }

    public void attack(Hero hero) {
    hero = new Hero("hero", 100); //形参指向了新的对象
    System.out.println("hero:"+hero);
    }

    public static void main(String[] args) {
    Hero teemo = new Hero("teemo", 383);
    Hero garen = new Hero("garen", 616);
    garen.attack(teemo);
    System.out.println("teemo:"+teemo);

    }

    }

    /**输出:
    hero:Hero@2a139a55
    teemo:Hero@15db9742
    */
    \n

    用图表示如下\"image-20210123185224792\"

    \n
  • \n
\n

类属性(又叫静态属性)

当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性
当一个属性被声明成类属性,那么所有的对象,都共享一个值

\n

对象属性: 又叫实例属性,非静态属性

\n
\n

与对象属性对比:
不同对象的 对象属性 的值都可能不一样。

\n
\n
    \n
  • 访问类属性:

    \n
    1
    类.类属性
    \n
    1
    2
    //访问对象属性:
    对象.对象属性
    \n
  • \n
  • 类属性与对象属性的选择

    \n
      \n
    • 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的,每个对象的name都是不同的
    • \n
    • 如果一个属性,所有的英雄都共享,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性
    • \n
    \n
  • \n
\n

类方法(静态方法)

类方法: 又叫做静态方法,即被static修饰的方法

\n

对象方法: 又叫实例方法,非静态方法

\n
\n

区别:

\n

调用一个对象方法,必须建立在有一个对象的前提的基础上
调用类方法,不需要对象的存在,直接就访问

\n
\n
    \n
  • 调用类方法

    \n
    1
    类.类方法
    \n
    1
    2
    //访问对象方法
    对象.对象方法
    \n
  • \n
  • 什么时候用类方法

    \n

    如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如:

    \n
    1
    2
    3
    public static void printGameDuration(){
    \tSystem.out.println("已经玩了50秒");
    }
    \n

    反之,如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法;

    \n

    注意:类方法中不能直接调用对象方法

    \n
  • \n
\n
\n

回到开头

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

类与对象

类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;

\n

用代码创建一个鸟类:

\n
1
2
3
4
5
6
7
public class Bird{
String name;
String feathers_color;
public void fly(){
System.out.println(name + "可以飞")
}
}
\n

对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;

\n

用代码来创建对象:

\n
1
2
Bird bird1 = new Bird();
Bird bird2 = new Bird();
\n

引用与指向

在下面创建的这一对象中

\n
1
Bird bird1 = new Bird();
\n

bird1存储的是对象的地址,因此bird1是对象的引用;这一地址指向了对象本身;

\n

\"image-20210123153305198\"

\n
    \n
  • 多个引用指向同一对象
  • \n
\n
1
2
3
Bird b1 = new Bird();
Bird b2 = b1;
Bird b3 = b1;
\n

\"image-20210123154400728\"

\n
    \n
  • 多个引用指向多个(不同)对象
  • \n
\n
1
2
3
Bird b1 = new Bird();
Bird b2 = new Bird();
Bird b3 = new Bird();
\n

\"image-20210123154635298\"

\n

继承

可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:

\n
1
2
3
public class ClassName2 extends ClassName1{

}
\n

例如:

\n

首先创建父类Item

\n
1
2
3
4
public class Item {
String name;
int price;
}
\n

创建子类Weapon继承Item

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Weapon extends Item{
int damage; //攻击力

public static void main(String[] args) {
Weapon infinityEdge = new Weapon();
infinityEdge.damage = 65; //damage属性在类Weapon中新设计的

infinityEdge.name = "无尽之刃";//name属性,是从Item中继承来的,就不需要重复设计了
infinityEdge.price = 3600;

}

}
\n

方法重载

方法的重载指的是方法名一样,但是参数类型或数量不一样

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ADHero extends Hero {
//无参数
public void attack() {
System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
}

\t//1个参数
public void attack(Hero h1) {
System.out.println(name + "对" + h1.name + "进行了一次攻击 ");
}
\t
//2个参数
public void attack(Hero h1, Hero h2) {
System.out.println(name + "同时对" + h1.name + "和" + h2.name + "进行了攻击 ");
}

public static void main(String[] args) {
ADHero bh = new ADHero();
bh.name = "赏金猎人";

Hero h1 = new Hero();
h1.name = "盖伦";
Hero h2 = new Hero();
h2.name = "提莫";

bh.attack(h1);
bh.attack(h1, h2);
}

}
\n

可变数量的参数

\n

采用可变数量的参数 只需要设计一个方法
public void attack(Hero... heros)
即可代表上述所有的方法了,此时 heros被当作一个数组,在方法里,使用操作数组的方式处理参数heros即可

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ADHero extends Hero {

public void attack() {
System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
}

// 可变数量的参数
public void attack(Hero... heros) {
for (int i = 0; i < heros.length; i++) {
System.out.println(name + " 攻击了 " + heros[i].name);

}
}

public static void main(String[] args) {
ADHero bh = new ADHero();
bh.name = "赏金猎人";

Hero h1 = new Hero();
h1.name = "盖伦";
Hero h2 = new Hero();
h2.name = "提莫";

bh.attack(h1);
bh.attack(h1, h2);

}

}
\n

构造方法

构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Hero {

String name;

float hp;

float armor;

int moveSpeed;

// 方法名和类名一样(包括大小写)
// 没有返回类型
public Hero() {
System.out.println("实例化一个对象的时候,必然调用构造方法");
}

public static void main(String[] args) {
//实例化一个对象的时候,必然调用构造方法
Hero h = new Hero();
}

}

//输出:实例化一个对象的时候,必然调用构造方法
\n

构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同

\n

this

this 代表当前对象,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Hero {

String name; //姓名

float hp; //血量

float armor; //护甲

int moveSpeed; //移动速度

//打印内存中的虚拟地址
public void showAddressInMemory(){
System.out.println("打印this看到的虚拟地址:"+this);
}

public static void main(String[] args) {
//---------------------------------------------
Hero garen = new Hero();
garen.name = "aaa";
System.out.println("打印对象看到的虚拟地址:"+garen);
garen.showAddressInMemory();

//-------------------------------------------
Hero teemo = new Hero();
teemo.name = "bbb";
System.out.println("打印对象看到的虚拟地址:"+teemo);
teemo.showAddressInMemory();
}

}

//输出:
/*
打印对象看到的虚拟地址:Hero@2a139a55
打印this看到的虚拟地址:Hero@2a139a55
打印对象看到的虚拟地址:Hero@15db9742
打印this看到的虚拟地址:Hero@15db9742
*/
\n

通过this 可以访问对象的属性、方法

\n

通过this()可以调用其他的构造方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Hero {

String name; // 姓名

float hp; // 血量

float armor; // 护甲

int moveSpeed; // 移动速度

// 带一个参数的构造方法
public Hero(String name) {
System.out.println("一个参数的构造方法");
this.name = name;
}

// 带两个参数的构造方法
public Hero(String name, float hp) {
this(name);
System.out.println("两个参数的构造方法");
this.hp = hp;
}

public static void main(String[] args) {
Hero teemo = new Hero("aaaa", 383); //两个参数

System.out.println(teemo.name);

}

}

/*输出:

一个参数的构造方法
两个参数的构造方法
aaaa
*/
\n

传参

    \n
  • 基本类型传参与引用类型传参

    \n

    基本类型传参,不多说

    \n

    引用类型传参,即传入参数为引用类型

    \n
  • \n
  • =的含义

    \n
      \n
    • 如果变量是基本类型,则 =表示赋值

      \n
    • \n
    • 如果变量是引用类型,= 表示指向的意思,比如

      \n
      1
      Hero h = new Hero();  //引用h,指向一个Hero对象
      \n
    • \n
    \n
  • \n
  • 引用类型传参

    \n

    形参与实参都指向同一对象

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Hero {

    String name;

    float hp;

    public Hero(String name, float hp) {
    this.name = name;
    this.hp = hp;
    }

    public void attack(Hero hero) {
    System.out.println("hero:"+hero);
    }

    public static void main(String[] args) {
    Hero teemo = new Hero("teemo", 383);
    Hero garen = new Hero("garen", 616);
    garen.attack(teemo);
    System.out.println("teemo:"+teemo);

    }

    }

    /**输出:
    hero:Hero@2a139a55
    teemo:Hero@2a139a55
    */
    \n

    用图表示如下\"image-20210123184909979\"

    \n

    但如果形参在内部指向了新的对象,实参是不会跟着改变的

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Hero {

    String name; // 姓名

    float hp; // 血量

    public Hero(String name, float hp) {
    this.name = name;
    this.hp = hp;
    }

    public void attack(Hero hero) {
    hero = new Hero("hero", 100); //形参指向了新的对象
    System.out.println("hero:"+hero);
    }

    public static void main(String[] args) {
    Hero teemo = new Hero("teemo", 383);
    Hero garen = new Hero("garen", 616);
    garen.attack(teemo);
    System.out.println("teemo:"+teemo);

    }

    }

    /**输出:
    hero:Hero@2a139a55
    teemo:Hero@15db9742
    */
    \n

    用图表示如下\"image-20210123185224792\"

    \n
  • \n
\n

类属性(又叫静态属性)

当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性
当一个属性被声明成类属性,那么所有的对象,都共享一个值

\n

对象属性: 又叫实例属性,非静态属性

\n
\n

与对象属性对比:
不同对象的 对象属性 的值都可能不一样。

\n
\n
    \n
  • 访问类属性:

    \n
    1
    类.类属性
    \n
    1
    2
    //访问对象属性:
    对象.对象属性
    \n
  • \n
  • 类属性与对象属性的选择

    \n
      \n
    • 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的,每个对象的name都是不同的
    • \n
    • 如果一个属性,所有的英雄都共享,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性
    • \n
    \n
  • \n
\n

类方法(静态方法)

类方法: 又叫做静态方法,即被static修饰的方法

\n

对象方法: 又叫实例方法,非静态方法

\n
\n

区别:

\n

调用一个对象方法,必须建立在有一个对象的前提的基础上
调用类方法,不需要对象的存在,直接就访问

\n
\n
    \n
  • 调用类方法

    \n
    1
    类.类方法
    \n
    1
    2
    //访问对象方法
    对象.对象方法
    \n
  • \n
  • 什么时候用类方法

    \n

    如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如:

    \n
    1
    2
    3
    public static void printGameDuration(){
    \tSystem.out.println("已经玩了50秒");
    }
    \n

    反之,如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法;

    \n

    注意:类方法中不能直接调用对象方法

    \n
  • \n
\n
\n

回到开头

\n"},{"title":"Http协议","description":"http协议介绍与工作原理","abbrlink":"374aa28c","date":"2021-04-16T06:31:33.000Z","_content":"\n\n\n\n\n\n\n## 介绍\n\n\n\n超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在[TCP](https://baike.baidu.com/item/TCP/33012)之上,如图\n\n![image-20210903214830491](https://gitee.com/ajream/images/raw/master/img/20210903214835_image-20210903214830491.png)\n\n\n\n文本:html, 字符串......\n\n超文本:图片、音乐……\n\nhttp端口:80\n\nhttps端口:443\n\n\n\n## Http请求与响应\n\nHTTP协议永远都是客户端发起请求,服务器回送响应。见下图:\n\n![image-20210903214905388](https://gitee.com/ajream/images/raw/master/img/20210903214907_image-20210903214905388.png)\n\n这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端\n\n\n\n请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体\n\n响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体\n\n\n\n**HTTP请求方式**\n\nGET:最常用的请求方式,使用GET方法应该只用在获取数据\n\nPOST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有\n\n除此以外还有`HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT` 这8种不常用的请求方式\n\n\n\n**URL**\n\nURL被称为:统一资源定位符\n\n其中通常包含信息:\n\n- 传送协议\n- 层级URL标记符号(为[//],固定不变)\n- 访问资源需要的凭证信息(可省略)\n- 服务器地址(通常为域名,有时为IP地址)\n- 端口号(可省略,默认使用http协议,端口为80)\n- 资源路径(以“/”字符区别路径中的每一个目录名称)\n- 查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)\n- 片段。以“#”字符为起点(可省略)\n\n例如:\n\n```\nhttp://www.luffycity.com:80/news/index.html?id=250&page=1\n```\n\n\n\n**HTTP状态码**\n\n状态码元由3位数字组成,表示请求是否被理解或被满足。\n\n\n\n1XX: 信息——请求已被服务器接收,继续处理\n\n2XX: 连接成功\n\n- 200:请求成功(其后是对GET和POST请求的应答文档)\n\n3XX: 重定向\n\n- 300:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址\n\n- 301:所请求的页面已经转移至新的url\n\n- 302:所请求的页面已经临时转移至新的url\n\n- 303:所请求的页面可在别的url下被找到\n\n \n\n4XX: 请求错误\n\n- 400:服务器不理解请求\n- 401:被请求的页面需要用户名和密码\n- 403:对被请求页面的访问被禁止\n- 404:服务器无法找到被请求的页面\n\n5XX: 服务器错误\n\n- 500:请求未完成,服务器遇到不可预知的情况。\n- 501:请求未完成。服务器不支持所请求的功能。\n- 503:请求未完成。服务器临时过载或宕机。\n- 504:网关超时\n\n\n\n\n\n**HTTP请求报文**\n\n\"image-20210903214930366\"\n\n例如:\n\n\"image-20210903214949259\"\n\n\n\n**HTTP响应报文**\n\n\"image-20210903215042801\"\n\n\n\n例如:\n\n\"image-20210903215124843\"\n\n\n\n## Http工作原理\n\nHTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。\n\nHTTP协议采用了请求/响应模型:\n\n- 客户端向服务器发送一个【请求报文】,请求报文包含请求的方法(get/post/...)、URL、协议版本、请求头部和请求数据。\n\n- 服务器以一个【状态行】作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。\n\n典型的HTTP事务处理有如下的过程: \n\n(1)客户与服务器建立连接:\n\n一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接\n\n(2)客户向服务器提出请求:\n\n通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。\n\n(3)服务器接受请求,并根据请求返回相应的文件作为应答:\n\nWeb服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。\n\n(4)客户与服务器关闭连接:\n\n- 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;\n\n- 若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;\n\n(5)客户端浏览器解析HTML内容:\n\n客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。\n\n然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。\n\n客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。\n\n","source":"_posts/javaEE/http.md","raw":"---\ntitle: Http协议\ntags:\n - Http\ncategories:\n - - java\n - javaEE\ndescription: http协议介绍与工作原理\nabbrlink: 374aa28c\ndate: 2021-04-16 14:31:33\n---\n\n\n\n\n\n\n\n## 介绍\n\n\n\n超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在[TCP](https://baike.baidu.com/item/TCP/33012)之上,如图\n\n![image-20210903214830491](https://gitee.com/ajream/images/raw/master/img/20210903214835_image-20210903214830491.png)\n\n\n\n文本:html, 字符串......\n\n超文本:图片、音乐……\n\nhttp端口:80\n\nhttps端口:443\n\n\n\n## Http请求与响应\n\nHTTP协议永远都是客户端发起请求,服务器回送响应。见下图:\n\n![image-20210903214905388](https://gitee.com/ajream/images/raw/master/img/20210903214907_image-20210903214905388.png)\n\n这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端\n\n\n\n请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体\n\n响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体\n\n\n\n**HTTP请求方式**\n\nGET:最常用的请求方式,使用GET方法应该只用在获取数据\n\nPOST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有\n\n除此以外还有`HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT` 这8种不常用的请求方式\n\n\n\n**URL**\n\nURL被称为:统一资源定位符\n\n其中通常包含信息:\n\n- 传送协议\n- 层级URL标记符号(为[//],固定不变)\n- 访问资源需要的凭证信息(可省略)\n- 服务器地址(通常为域名,有时为IP地址)\n- 端口号(可省略,默认使用http协议,端口为80)\n- 资源路径(以“/”字符区别路径中的每一个目录名称)\n- 查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)\n- 片段。以“#”字符为起点(可省略)\n\n例如:\n\n```\nhttp://www.luffycity.com:80/news/index.html?id=250&page=1\n```\n\n\n\n**HTTP状态码**\n\n状态码元由3位数字组成,表示请求是否被理解或被满足。\n\n\n\n1XX: 信息——请求已被服务器接收,继续处理\n\n2XX: 连接成功\n\n- 200:请求成功(其后是对GET和POST请求的应答文档)\n\n3XX: 重定向\n\n- 300:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址\n\n- 301:所请求的页面已经转移至新的url\n\n- 302:所请求的页面已经临时转移至新的url\n\n- 303:所请求的页面可在别的url下被找到\n\n \n\n4XX: 请求错误\n\n- 400:服务器不理解请求\n- 401:被请求的页面需要用户名和密码\n- 403:对被请求页面的访问被禁止\n- 404:服务器无法找到被请求的页面\n\n5XX: 服务器错误\n\n- 500:请求未完成,服务器遇到不可预知的情况。\n- 501:请求未完成。服务器不支持所请求的功能。\n- 503:请求未完成。服务器临时过载或宕机。\n- 504:网关超时\n\n\n\n\n\n**HTTP请求报文**\n\n\"image-20210903214930366\"\n\n例如:\n\n\"image-20210903214949259\"\n\n\n\n**HTTP响应报文**\n\n\"image-20210903215042801\"\n\n\n\n例如:\n\n\"image-20210903215124843\"\n\n\n\n## Http工作原理\n\nHTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。\n\nHTTP协议采用了请求/响应模型:\n\n- 客户端向服务器发送一个【请求报文】,请求报文包含请求的方法(get/post/...)、URL、协议版本、请求头部和请求数据。\n\n- 服务器以一个【状态行】作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。\n\n典型的HTTP事务处理有如下的过程: \n\n(1)客户与服务器建立连接:\n\n一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接\n\n(2)客户向服务器提出请求:\n\n通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。\n\n(3)服务器接受请求,并根据请求返回相应的文件作为应答:\n\nWeb服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。\n\n(4)客户与服务器关闭连接:\n\n- 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;\n\n- 若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;\n\n(5)客户端浏览器解析HTML内容:\n\n客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。\n\n然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。\n\n客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。\n\n","slug":"javaEE/http","published":1,"updated":"2021-09-03T13:53:35.404Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qj005fakvegpvj86dz","content":"

介绍

超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上,如图

\n

\"image-20210903214830491\"

\n

文本:html, 字符串……

\n

超文本:图片、音乐……

\n

http端口:80

\n

https端口:443

\n

Http请求与响应

HTTP协议永远都是客户端发起请求,服务器回送响应。见下图:

\n

\"image-20210903214905388\"

\n

这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端

\n

请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体

\n

响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体

\n

HTTP请求方式

\n

GET:最常用的请求方式,使用GET方法应该只用在获取数据

\n

POST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有

\n

除此以外还有HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT 这8种不常用的请求方式

\n

URL

\n

URL被称为:统一资源定位符

\n

其中通常包含信息:

\n
    \n
  • 传送协议
  • \n
  • 层级URL标记符号(为[//],固定不变)
  • \n
  • 访问资源需要的凭证信息(可省略)
  • \n
  • 服务器地址(通常为域名,有时为IP地址)
  • \n
  • 端口号(可省略,默认使用http协议,端口为80)
  • \n
  • 资源路径(以“/”字符区别路径中的每一个目录名称)
  • \n
  • 查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
  • \n
  • 片段。以“#”字符为起点(可省略)
  • \n
\n

例如:

\n
1
http://www.luffycity.com:80/news/index.html?id=250&page=1
\n

HTTP状态码

\n

状态码元由3位数字组成,表示请求是否被理解或被满足。

\n

1XX: 信息——请求已被服务器接收,继续处理

\n

2XX: 连接成功

\n
    \n
  • 200:请求成功(其后是对GET和POST请求的应答文档)
  • \n
\n

3XX: 重定向

\n
    \n
  • 300:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址

    \n
  • \n
  • 301:所请求的页面已经转移至新的url

    \n
  • \n
  • 302:所请求的页面已经临时转移至新的url

    \n
  • \n
  • 303:所请求的页面可在别的url下被找到

    \n
  • \n
\n

4XX: 请求错误

\n
    \n
  • 400:服务器不理解请求
  • \n
  • 401:被请求的页面需要用户名和密码
  • \n
  • 403:对被请求页面的访问被禁止
  • \n
  • 404:服务器无法找到被请求的页面
  • \n
\n

5XX: 服务器错误

\n
    \n
  • 500:请求未完成,服务器遇到不可预知的情况。
  • \n
  • 501:请求未完成。服务器不支持所请求的功能。
  • \n
  • 503:请求未完成。服务器临时过载或宕机。
  • \n
  • 504:网关超时
  • \n
\n

HTTP请求报文

\n

\"image-20210903214930366\"

\n

例如:

\n

\"image-20210903214949259\"

\n

HTTP响应报文

\n

\"image-20210903215042801\"

\n

例如:

\n

\"image-20210903215124843\"

\n

Http工作原理

HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。

\n

HTTP协议采用了请求/响应模型:

\n
    \n
  • 客户端向服务器发送一个【请求报文】,请求报文包含请求的方法(get/post/…)、URL、协议版本、请求头部和请求数据。

    \n
  • \n
  • 服务器以一个【状态行】作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。

    \n
  • \n
\n

典型的HTTP事务处理有如下的过程:

\n

(1)客户与服务器建立连接:

\n

一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接

\n

(2)客户向服务器提出请求:

\n

通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。

\n

(3)服务器接受请求,并根据请求返回相应的文件作为应答:

\n

Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。

\n

(4)客户与服务器关闭连接:

\n
    \n
  • 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;

    \n
  • \n
  • 若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;

    \n
  • \n
\n

(5)客户端浏览器解析HTML内容:

\n

客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。

\n

然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。

\n

客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"

介绍

超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上,如图

\n

\"image-20210903214830491\"

\n

文本:html, 字符串……

\n

超文本:图片、音乐……

\n

http端口:80

\n

https端口:443

\n

Http请求与响应

HTTP协议永远都是客户端发起请求,服务器回送响应。见下图:

\n

\"image-20210903214905388\"

\n

这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端

\n

请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体

\n

响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体

\n

HTTP请求方式

\n

GET:最常用的请求方式,使用GET方法应该只用在获取数据

\n

POST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有

\n

除此以外还有HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT 这8种不常用的请求方式

\n

URL

\n

URL被称为:统一资源定位符

\n

其中通常包含信息:

\n
    \n
  • 传送协议
  • \n
  • 层级URL标记符号(为[//],固定不变)
  • \n
  • 访问资源需要的凭证信息(可省略)
  • \n
  • 服务器地址(通常为域名,有时为IP地址)
  • \n
  • 端口号(可省略,默认使用http协议,端口为80)
  • \n
  • 资源路径(以“/”字符区别路径中的每一个目录名称)
  • \n
  • 查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
  • \n
  • 片段。以“#”字符为起点(可省略)
  • \n
\n

例如:

\n
1
http://www.luffycity.com:80/news/index.html?id=250&page=1
\n

HTTP状态码

\n

状态码元由3位数字组成,表示请求是否被理解或被满足。

\n

1XX: 信息——请求已被服务器接收,继续处理

\n

2XX: 连接成功

\n
    \n
  • 200:请求成功(其后是对GET和POST请求的应答文档)
  • \n
\n

3XX: 重定向

\n
    \n
  • 300:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址

    \n
  • \n
  • 301:所请求的页面已经转移至新的url

    \n
  • \n
  • 302:所请求的页面已经临时转移至新的url

    \n
  • \n
  • 303:所请求的页面可在别的url下被找到

    \n
  • \n
\n

4XX: 请求错误

\n
    \n
  • 400:服务器不理解请求
  • \n
  • 401:被请求的页面需要用户名和密码
  • \n
  • 403:对被请求页面的访问被禁止
  • \n
  • 404:服务器无法找到被请求的页面
  • \n
\n

5XX: 服务器错误

\n
    \n
  • 500:请求未完成,服务器遇到不可预知的情况。
  • \n
  • 501:请求未完成。服务器不支持所请求的功能。
  • \n
  • 503:请求未完成。服务器临时过载或宕机。
  • \n
  • 504:网关超时
  • \n
\n

HTTP请求报文

\n

\"image-20210903214930366\"

\n

例如:

\n

\"image-20210903214949259\"

\n

HTTP响应报文

\n

\"image-20210903215042801\"

\n

例如:

\n

\"image-20210903215124843\"

\n

Http工作原理

HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。

\n

HTTP协议采用了请求/响应模型:

\n
    \n
  • 客户端向服务器发送一个【请求报文】,请求报文包含请求的方法(get/post/…)、URL、协议版本、请求头部和请求数据。

    \n
  • \n
  • 服务器以一个【状态行】作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。

    \n
  • \n
\n

典型的HTTP事务处理有如下的过程:

\n

(1)客户与服务器建立连接:

\n

一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接

\n

(2)客户向服务器提出请求:

\n

通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。

\n

(3)服务器接受请求,并根据请求返回相应的文件作为应答:

\n

Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。

\n

(4)客户与服务器关闭连接:

\n
    \n
  • 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;

    \n
  • \n
  • 若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;

    \n
  • \n
\n

(5)客户端浏览器解析HTML内容:

\n

客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。

\n

然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。

\n

客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

\n"},{"title":"11-数字与字符串相互转换","abbrlink":"38b36403","date":"2021-02-13T10:58:18.000Z","description":"java的数字类型与String类型的转换实际依靠封装类里面的方法","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 数字⇿字符串\n\n## 数字转字符串(2种方法)\n\n- 使用String类的静态方法`valueOf`\n- 先把基本类型装箱为对象,然后调用对象的`toString`\n\n```java\nint i = 5;\n \n//方法1\nString str = String.valueOf(i);\n \n//方法2\nInteger it = i;\nString str2 = it.toString();\n```\n\n\n\n## 字符串转数字\n\n- 调用Integer的静态方法`parseInt`\n\n```java\nString str = \"999\";\n \nint i= Integer.parseInt(str);\n```\n\n\n\n","source":"_posts/javaSE/11-数字与字符串相互转换.md","raw":"---\ntitle: 11-数字与字符串相互转换\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 38b36403\ndate: 2021-02-13 18:58:18\ndescription: java的数字类型与String类型的转换实际依靠封装类里面的方法\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 数字⇿字符串\n\n## 数字转字符串(2种方法)\n\n- 使用String类的静态方法`valueOf`\n- 先把基本类型装箱为对象,然后调用对象的`toString`\n\n```java\nint i = 5;\n \n//方法1\nString str = String.valueOf(i);\n \n//方法2\nInteger it = i;\nString str2 = it.toString();\n```\n\n\n\n## 字符串转数字\n\n- 调用Integer的静态方法`parseInt`\n\n```java\nString str = \"999\";\n \nint i= Integer.parseInt(str);\n```\n\n\n\n","slug":"javaSE/11-数字与字符串相互转换","published":1,"updated":"2021-08-29T15:31:24.118Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ql005jakve8xo8cwcv","content":"

数字⇿字符串

数字转字符串(2种方法)

    \n
  • 使用String类的静态方法valueOf
  • \n
  • 先把基本类型装箱为对象,然后调用对象的toString
  • \n
\n
1
2
3
4
5
6
7
8
int i = 5;

//方法1
String str = String.valueOf(i);

//方法2
Integer it = i;
String str2 = it.toString();
\n

字符串转数字

    \n
  • 调用Integer的静态方法parseInt
  • \n
\n
1
2
3
String str = "999";

int i= Integer.parseInt(str);
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

数字⇿字符串

数字转字符串(2种方法)

    \n
  • 使用String类的静态方法valueOf
  • \n
  • 先把基本类型装箱为对象,然后调用对象的toString
  • \n
\n
1
2
3
4
5
6
7
8
int i = 5;

//方法1
String str = String.valueOf(i);

//方法2
Integer it = i;
String str2 = it.toString();
\n

字符串转数字

    \n
  • 调用Integer的静态方法parseInt
  • \n
\n
1
2
3
String str = "999";

int i= Integer.parseInt(str);
\n"},{"title":"Tomcat基础","abbrlink":"530cfffd","date":"2021-06-11T05:31:26.000Z","description":"Web基础知识、服务器、Tomcat介绍、安装与使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210901182628_tomcat.png","_content":"\n# Tomcat了解\n\n\n\n## web概念\n\n1. 软件架构\n - C/S:客户端/服务器端(QQ、360。。。)\n - B/S:浏览器/服务器端(京东、淘宝、网易。。。)\n2. 资源分类\n - 静态资源(所有用户访问得到的结果一样)\n - 动态资源(不同用户访问的结果不同)\n3. 网络通信三要素\n - 协议\n - tcp:安全协议、三次握手、速度较慢\n - udp:不安全、速度快\n - ip:电子设备在网络中唯一标识\n - 端口号\n\n\n\n## 常见web服务器软件\n\n1. webLogic:大型JavaEE服务器,属于Oracle公司,收费\n2. webSphere:IBM公司\n3. JBOSS:JBOSS公司\n4. Tomcat:Apache组织\n\n\n\n## Tomcat介绍\n\n[Tomcat下载](https://tomcat.apache.org/download-80.cgi)\n\n\n\nTomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用[服务器](https://baike.baidu.com/item/服务器),在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应[HTML](https://baike.baidu.com/item/HTML)页面的访问请求。\n\n实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。\n\n\n\n### 目录结构\n\n![image-20210903200423036](https://gitee.com/ajream/images/raw/master/img/20210903200427_image-20210903200423036.png)\n\n\n\n重点,服务器配置文件 `/conf/server.xml`\n\n配置访问端口号,默认是 `8080`,如果被占用了可以改为其它端口\n\n```xml\n\n```\n\n配置服务器地址\n\nlocalhost: 实际是指向域名 `127.0.0.1`\n\n```xml\n\n```\n\n{%note primary flat%}\n\n面试题:访问一个网站的流程?\n\n大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)\n\n1. 浏览器输入域名,回车\n2. 浏览器查找本地 `C:\\Windows\\System32\\drivers\\etc\\host`文件,进行域名解析\n3. 如果没有找到,则去DNS服务器查找\n\n{%endnote%}\n\n\n\n## Tomcat使用\n\n\n\n### 启动tomcat\n\n运行bin目录下的批处理文件 `startup.bat`(确认当前是jdk1.8或以上,用`java -version`查看)\n\n\n\n### 部署网页\n\n例如部署一个test.html文件\n\n1. 把test.html文件复制到**D:\\tomcat\\webapps\\ROOT** 目录下\n2. 启动tomcat\n3. 访问 http://127.0.0.1:8080/test.html\n\n\n\n### 改端口号\n\n8080是tomcat默认端口号,而平时上网默认使用80端口\n\ntomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。\n\n修改端口号步骤:\n\n1. 打开server.xml文件\n2. `ctrl+f`查询8080,将下面这句代码的`8080`修改为其他端口号,如80,8081,8082\n\n```xml\n\n```\n\n接着就可以直接通过 http://127.0.0.1/test.html访问网页了。\n\n80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。\n\n\n\n### 端口被占用\n\n端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可\n\n- 查看80端口被哪些程序占用\n\n ```shell\n netstat -ano|findstr \"80\"\n ```\n\n ![image-20210408072223224](https://gitee.com/ajream/images/raw/master/img/image-20210408072223224.png)\n\n 第一行就是占用端口“80”的进程(其他只是含有80这个字符串而已),最后一列对应的进程的pid\n\n- 根据pid查询对应应用程序\n\n ```sh\n tasklist|findstr \"4\"\n ```\n\n ![image-20210408072728546](https://gitee.com/ajream/images/raw/master/img/image-20210408072728546.png)\n\n 第一个system就是占用的程序\n\n- 根据名称结束该进程\n\n ```shell\n taskkill /f /t /im java.exe\n ```\n\n 结束java.exe\n\n 结束成功会提示:\n 成功: 已终止 。。。\n\n 或者通过pid终止:\n\n ```shell\n taskkill /f /pid 1828\n ```\n\n \n\n ![根据名称 结束该程序](https://stepimagewm.how2j.cn/1565.png)\n\n\n\n\n\n> 也可以把配置文件 `server.xml` 的默认端口号更改\n>\n\n","source":"_posts/javaEE/Tomcat基础.md","raw":"---\ntitle: Tomcat基础\ntags:\n - Tomcat\ncategories:\n - - java\n - javaEE\nabbrlink: 530cfffd\ndate: 2021-06-11 13:31:26\ndescription: Web基础知识、服务器、Tomcat介绍、安装与使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210901182628_tomcat.png\n---\n\n# Tomcat了解\n\n\n\n## web概念\n\n1. 软件架构\n - C/S:客户端/服务器端(QQ、360。。。)\n - B/S:浏览器/服务器端(京东、淘宝、网易。。。)\n2. 资源分类\n - 静态资源(所有用户访问得到的结果一样)\n - 动态资源(不同用户访问的结果不同)\n3. 网络通信三要素\n - 协议\n - tcp:安全协议、三次握手、速度较慢\n - udp:不安全、速度快\n - ip:电子设备在网络中唯一标识\n - 端口号\n\n\n\n## 常见web服务器软件\n\n1. webLogic:大型JavaEE服务器,属于Oracle公司,收费\n2. webSphere:IBM公司\n3. JBOSS:JBOSS公司\n4. Tomcat:Apache组织\n\n\n\n## Tomcat介绍\n\n[Tomcat下载](https://tomcat.apache.org/download-80.cgi)\n\n\n\nTomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用[服务器](https://baike.baidu.com/item/服务器),在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应[HTML](https://baike.baidu.com/item/HTML)页面的访问请求。\n\n实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。\n\n\n\n### 目录结构\n\n![image-20210903200423036](https://gitee.com/ajream/images/raw/master/img/20210903200427_image-20210903200423036.png)\n\n\n\n重点,服务器配置文件 `/conf/server.xml`\n\n配置访问端口号,默认是 `8080`,如果被占用了可以改为其它端口\n\n```xml\n\n```\n\n配置服务器地址\n\nlocalhost: 实际是指向域名 `127.0.0.1`\n\n```xml\n\n```\n\n{%note primary flat%}\n\n面试题:访问一个网站的流程?\n\n大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)\n\n1. 浏览器输入域名,回车\n2. 浏览器查找本地 `C:\\Windows\\System32\\drivers\\etc\\host`文件,进行域名解析\n3. 如果没有找到,则去DNS服务器查找\n\n{%endnote%}\n\n\n\n## Tomcat使用\n\n\n\n### 启动tomcat\n\n运行bin目录下的批处理文件 `startup.bat`(确认当前是jdk1.8或以上,用`java -version`查看)\n\n\n\n### 部署网页\n\n例如部署一个test.html文件\n\n1. 把test.html文件复制到**D:\\tomcat\\webapps\\ROOT** 目录下\n2. 启动tomcat\n3. 访问 http://127.0.0.1:8080/test.html\n\n\n\n### 改端口号\n\n8080是tomcat默认端口号,而平时上网默认使用80端口\n\ntomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。\n\n修改端口号步骤:\n\n1. 打开server.xml文件\n2. `ctrl+f`查询8080,将下面这句代码的`8080`修改为其他端口号,如80,8081,8082\n\n```xml\n\n```\n\n接着就可以直接通过 http://127.0.0.1/test.html访问网页了。\n\n80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。\n\n\n\n### 端口被占用\n\n端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可\n\n- 查看80端口被哪些程序占用\n\n ```shell\n netstat -ano|findstr \"80\"\n ```\n\n ![image-20210408072223224](https://gitee.com/ajream/images/raw/master/img/image-20210408072223224.png)\n\n 第一行就是占用端口“80”的进程(其他只是含有80这个字符串而已),最后一列对应的进程的pid\n\n- 根据pid查询对应应用程序\n\n ```sh\n tasklist|findstr \"4\"\n ```\n\n ![image-20210408072728546](https://gitee.com/ajream/images/raw/master/img/image-20210408072728546.png)\n\n 第一个system就是占用的程序\n\n- 根据名称结束该进程\n\n ```shell\n taskkill /f /t /im java.exe\n ```\n\n 结束java.exe\n\n 结束成功会提示:\n 成功: 已终止 。。。\n\n 或者通过pid终止:\n\n ```shell\n taskkill /f /pid 1828\n ```\n\n \n\n ![根据名称 结束该程序](https://stepimagewm.how2j.cn/1565.png)\n\n\n\n\n\n> 也可以把配置文件 `server.xml` 的默认端口号更改\n>\n\n","slug":"javaEE/Tomcat基础","published":1,"updated":"2021-09-03T12:21:26.646Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ql005makve7a1dc654","content":"

Tomcat了解

web概念

    \n
  1. 软件架构
      \n
    • C/S:客户端/服务器端(QQ、360。。。)
    • \n
    • B/S:浏览器/服务器端(京东、淘宝、网易。。。)
    • \n
    \n
  2. \n
  3. 资源分类
      \n
    • 静态资源(所有用户访问得到的结果一样)
    • \n
    • 动态资源(不同用户访问的结果不同)
    • \n
    \n
  4. \n
  5. 网络通信三要素
      \n
    • 协议
        \n
      • tcp:安全协议、三次握手、速度较慢
      • \n
      • udp:不安全、速度快
      • \n
      \n
    • \n
    • ip:电子设备在网络中唯一标识
    • \n
    • 端口号
    • \n
    \n
  6. \n
\n

常见web服务器软件

    \n
  1. webLogic:大型JavaEE服务器,属于Oracle公司,收费
  2. \n
  3. webSphere:IBM公司
  4. \n
  5. JBOSS:JBOSS公司
  6. \n
  7. Tomcat:Apache组织
  8. \n
\n

Tomcat介绍

Tomcat下载

\n

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML页面的访问请求。

\n

实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。

\n

目录结构

\"image-20210903200423036\"

\n

重点,服务器配置文件 /conf/server.xml

\n

配置访问端口号,默认是 8080,如果被占用了可以改为其它端口

\n
1
2
3
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
\n

配置服务器地址

\n

localhost: 实际是指向域名 127.0.0.1

\n
1
2
<Host name="localhost"  appBase="webapps"
unpackWARs="true" autoDeploy="true">
\n

面试题:访问一个网站的流程?

\n

大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)

\n
    \n
  1. 浏览器输入域名,回车
  2. \n
  3. 浏览器查找本地 C:\\Windows\\System32\\drivers\\etc\\host文件,进行域名解析
  4. \n
  5. 如果没有找到,则去DNS服务器查找
  6. \n
\n
\n

Tomcat使用

启动tomcat

运行bin目录下的批处理文件 startup.bat(确认当前是jdk1.8或以上,用java -version查看)

\n

部署网页

例如部署一个test.html文件

\n
    \n
  1. 把test.html文件复制到D:\\tomcat\\webapps\\ROOT 目录下
  2. \n
  3. 启动tomcat
  4. \n
  5. 访问 http://127.0.0.1:8080/test.html
  6. \n
\n

改端口号

8080是tomcat默认端口号,而平时上网默认使用80端口

\n

tomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。

\n

修改端口号步骤:

\n
    \n
  1. 打开server.xml文件
  2. \n
  3. ctrl+f查询8080,将下面这句代码的8080修改为其他端口号,如80,8081,8082
  4. \n
\n
1
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8543" />
\n

接着就可以直接通过 http://127.0.0.1/test.html访问网页了。

\n

80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。

\n

端口被占用

端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可

\n
    \n
  • 查看80端口被哪些程序占用

    \n
    1
    netstat -ano|findstr "80"
    \n

    \"image-20210408072223224\"

    \n

    第一行就是占用端口“80”的进程(其他只是含有80这个字符串而已),最后一列对应的进程的pid

    \n
  • \n
  • 根据pid查询对应应用程序

    \n
    1
    tasklist|findstr "4"
    \n

    \"image-20210408072728546\"

    \n

    第一个system就是占用的程序

    \n
  • \n
  • 根据名称结束该进程

    \n
    1
    taskkill /f /t /im java.exe
    \n

    结束java.exe

    \n

    结束成功会提示:
    成功: 已终止 。。。

    \n

    或者通过pid终止:

    \n
    1
    taskkill /f /pid 1828
    \n
  • \n
\n

\"根据名称

\n
\n

也可以把配置文件 server.xml 的默认端口号更改

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Tomcat了解

web概念

    \n
  1. 软件架构
      \n
    • C/S:客户端/服务器端(QQ、360。。。)
    • \n
    • B/S:浏览器/服务器端(京东、淘宝、网易。。。)
    • \n
    \n
  2. \n
  3. 资源分类
      \n
    • 静态资源(所有用户访问得到的结果一样)
    • \n
    • 动态资源(不同用户访问的结果不同)
    • \n
    \n
  4. \n
  5. 网络通信三要素
      \n
    • 协议
        \n
      • tcp:安全协议、三次握手、速度较慢
      • \n
      • udp:不安全、速度快
      • \n
      \n
    • \n
    • ip:电子设备在网络中唯一标识
    • \n
    • 端口号
    • \n
    \n
  6. \n
\n

常见web服务器软件

    \n
  1. webLogic:大型JavaEE服务器,属于Oracle公司,收费
  2. \n
  3. webSphere:IBM公司
  4. \n
  5. JBOSS:JBOSS公司
  6. \n
  7. Tomcat:Apache组织
  8. \n
\n

Tomcat介绍

Tomcat下载

\n

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML页面的访问请求。

\n

实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。

\n

目录结构

\"image-20210903200423036\"

\n

重点,服务器配置文件 /conf/server.xml

\n

配置访问端口号,默认是 8080,如果被占用了可以改为其它端口

\n
1
2
3
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
\n

配置服务器地址

\n

localhost: 实际是指向域名 127.0.0.1

\n
1
2
<Host name="localhost"  appBase="webapps"
unpackWARs="true" autoDeploy="true">
\n

面试题:访问一个网站的流程?

\n

大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)

\n
    \n
  1. 浏览器输入域名,回车
  2. \n
  3. 浏览器查找本地 C:\\Windows\\System32\\drivers\\etc\\host文件,进行域名解析
  4. \n
  5. 如果没有找到,则去DNS服务器查找
  6. \n
\n
\n

Tomcat使用

启动tomcat

运行bin目录下的批处理文件 startup.bat(确认当前是jdk1.8或以上,用java -version查看)

\n

部署网页

例如部署一个test.html文件

\n
    \n
  1. 把test.html文件复制到D:\\tomcat\\webapps\\ROOT 目录下
  2. \n
  3. 启动tomcat
  4. \n
  5. 访问 http://127.0.0.1:8080/test.html
  6. \n
\n

改端口号

8080是tomcat默认端口号,而平时上网默认使用80端口

\n

tomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。

\n

修改端口号步骤:

\n
    \n
  1. 打开server.xml文件
  2. \n
  3. ctrl+f查询8080,将下面这句代码的8080修改为其他端口号,如80,8081,8082
  4. \n
\n
1
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8543" />
\n

接着就可以直接通过 http://127.0.0.1/test.html访问网页了。

\n

80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。

\n

端口被占用

端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可

\n
    \n
  • 查看80端口被哪些程序占用

    \n
    1
    netstat -ano|findstr "80"
    \n

    \"image-20210408072223224\"

    \n

    第一行就是占用端口“80”的进程(其他只是含有80这个字符串而已),最后一列对应的进程的pid

    \n
  • \n
  • 根据pid查询对应应用程序

    \n
    1
    tasklist|findstr "4"
    \n

    \"image-20210408072728546\"

    \n

    第一个system就是占用的程序

    \n
  • \n
  • 根据名称结束该进程

    \n
    1
    taskkill /f /t /im java.exe
    \n

    结束java.exe

    \n

    结束成功会提示:
    成功: 已终止 。。。

    \n

    或者通过pid终止:

    \n
    1
    taskkill /f /pid 1828
    \n
  • \n
\n

\"根据名称

\n
\n

也可以把配置文件 server.xml 的默认端口号更改

\n
\n"},{"title":"10-装箱与拆箱","abbrlink":"3aac01ca","date":"2021-02-13T11:33:27.000Z","description":"java的基本数据类型实际上都有一个封装类,这样就可以更方便的操作这些基本数据类型,封装类与基本数据类型的转换即为装箱、拆箱","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 装箱与拆箱\n\n## 封装类\n\n> 所有的**基本类型**,都有对应的**类类型**\n> 比如int对应的类是Integer\n> 这种类就叫做封装类\n\n```java\nint i = 5;\n \n//把一个基本类型的变量,转换为Integer对象\nInteger it = new Integer(i);\n\n//把一个Integer对象,转换为一个基本类型的int\nint i2 = it.intValue();\n```\n\n## 10.2 Number类\n\n> 数字封装类有\n>\n> - Byte Short Integer Long\n> - Float Double\n>\n> 这些类都是抽象类`Number`的子类\n\n## 基本类型<->封装类型\n\n- 基本类型转换成封装类型\n\n```java\nint i = 5;\n \nInteger it = new Integer(i); //i转换成封装类型it\n \n```\n\n- 封装类型转换成基本类型\n\n```java\nint i = 5;\n\nInteger it = new Integer(i); //i转换成封装类型it\n\nint i2 = it.intValue(); //封装类型转it换成基本类型i2,i2与i是一样的\n```\n\n\n\n## 自动装箱与拆箱\n\n- 自动装箱:通过`=`符号自动把 基本类型 —> 类类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n ```\n\n- 自动拆箱:与自动装箱相反,通过`=`符号自动把 类类型 —> 基本类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n \n int i1 = it;\t\t//自动拆箱\n ```\n\n\n\n## int的最大值/最小值\n\nint的最大值可以通过其对应的封装类`Integer.MAX_VALUE`获取;\n\n同理,最小值通过 `Integer.MIN_VALUE` 获取;\n\n\n\n","source":"_posts/javaSE/10-装箱与拆箱.md","raw":"---\ntitle: 10-装箱与拆箱\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 3aac01ca\ndate: 2021-02-13 19:33:27\ndescription: java的基本数据类型实际上都有一个封装类,这样就可以更方便的操作这些基本数据类型,封装类与基本数据类型的转换即为装箱、拆箱\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 装箱与拆箱\n\n## 封装类\n\n> 所有的**基本类型**,都有对应的**类类型**\n> 比如int对应的类是Integer\n> 这种类就叫做封装类\n\n```java\nint i = 5;\n \n//把一个基本类型的变量,转换为Integer对象\nInteger it = new Integer(i);\n\n//把一个Integer对象,转换为一个基本类型的int\nint i2 = it.intValue();\n```\n\n## 10.2 Number类\n\n> 数字封装类有\n>\n> - Byte Short Integer Long\n> - Float Double\n>\n> 这些类都是抽象类`Number`的子类\n\n## 基本类型<->封装类型\n\n- 基本类型转换成封装类型\n\n```java\nint i = 5;\n \nInteger it = new Integer(i); //i转换成封装类型it\n \n```\n\n- 封装类型转换成基本类型\n\n```java\nint i = 5;\n\nInteger it = new Integer(i); //i转换成封装类型it\n\nint i2 = it.intValue(); //封装类型转it换成基本类型i2,i2与i是一样的\n```\n\n\n\n## 自动装箱与拆箱\n\n- 自动装箱:通过`=`符号自动把 基本类型 —> 类类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n ```\n\n- 自动拆箱:与自动装箱相反,通过`=`符号自动把 类类型 —> 基本类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n \n int i1 = it;\t\t//自动拆箱\n ```\n\n\n\n## int的最大值/最小值\n\nint的最大值可以通过其对应的封装类`Integer.MAX_VALUE`获取;\n\n同理,最小值通过 `Integer.MIN_VALUE` 获取;\n\n\n\n","slug":"javaSE/10-装箱与拆箱","published":1,"updated":"2021-08-29T15:31:20.436Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qn005qakve26rcd19s","content":"

装箱与拆箱

封装类

\n

所有的基本类型,都有对应的类类型
比如int对应的类是Integer
这种类就叫做封装类

\n
\n
1
2
3
4
5
6
7
int i = 5;

//把一个基本类型的变量,转换为Integer对象
Integer it = new Integer(i);

//把一个Integer对象,转换为一个基本类型的int
int i2 = it.intValue();
\n

10.2 Number类

\n

数字封装类有

\n
    \n
  • Byte Short Integer Long
  • \n
  • Float Double
  • \n
\n

这些类都是抽象类Number的子类

\n
\n

基本类型<->封装类型

    \n
  • 基本类型转换成封装类型
  • \n
\n
1
2
3
4
int i = 5;

Integer it = new Integer(i); //i转换成封装类型it

\n
    \n
  • 封装类型转换成基本类型
  • \n
\n
1
2
3
4
5
int i = 5;

Integer it = new Integer(i); //i转换成封装类型it

int i2 = it.intValue(); //封装类型转it换成基本类型i2,i2与i是一样的
\n

自动装箱与拆箱

    \n
  • 自动装箱:通过=符号自动把 基本类型 —> 类类型

    \n
    1
    2
    int i = 5;
    Integer it = i; //自动装箱
    \n
  • \n
  • 自动拆箱:与自动装箱相反,通过=符号自动把 类类型 —> 基本类型

    \n
    1
    2
    3
    4
    int i = 5;
    Integer it = i; //自动装箱

    int i1 = it;\t\t//自动拆箱
    \n
  • \n
\n

int的最大值/最小值

int的最大值可以通过其对应的封装类Integer.MAX_VALUE获取;

\n

同理,最小值通过 Integer.MIN_VALUE 获取;

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

装箱与拆箱

封装类

\n

所有的基本类型,都有对应的类类型
比如int对应的类是Integer
这种类就叫做封装类

\n
\n
1
2
3
4
5
6
7
int i = 5;

//把一个基本类型的变量,转换为Integer对象
Integer it = new Integer(i);

//把一个Integer对象,转换为一个基本类型的int
int i2 = it.intValue();
\n

10.2 Number类

\n

数字封装类有

\n
    \n
  • Byte Short Integer Long
  • \n
  • Float Double
  • \n
\n

这些类都是抽象类Number的子类

\n
\n

基本类型<->封装类型

    \n
  • 基本类型转换成封装类型
  • \n
\n
1
2
3
4
int i = 5;

Integer it = new Integer(i); //i转换成封装类型it

\n
    \n
  • 封装类型转换成基本类型
  • \n
\n
1
2
3
4
5
int i = 5;

Integer it = new Integer(i); //i转换成封装类型it

int i2 = it.intValue(); //封装类型转it换成基本类型i2,i2与i是一样的
\n

自动装箱与拆箱

    \n
  • 自动装箱:通过=符号自动把 基本类型 —> 类类型

    \n
    1
    2
    int i = 5;
    Integer it = i; //自动装箱
    \n
  • \n
  • 自动拆箱:与自动装箱相反,通过=符号自动把 类类型 —> 基本类型

    \n
    1
    2
    3
    4
    int i = 5;
    Integer it = i; //自动装箱

    int i1 = it;\t\t//自动拆箱
    \n
  • \n
\n

int的最大值/最小值

int的最大值可以通过其对应的封装类Integer.MAX_VALUE获取;

\n

同理,最小值通过 Integer.MIN_VALUE 获取;

\n"},{"title":"15-异常处理","abbrlink":"f5c95c","date":"2021-02-15T02:46:30.000Z","description":"java中的异常处理","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 异常处理\n\n## 异常定义\n\n导致程序的正常流程被中断的事件,叫做异常\n\n异常处理常见手段:try catch finally throws\n\n\n\n例如:文件不存在异常\n\n```java\npackage ExceptionProcess;\n\nimport java.io.File;\nimport java.io.FileInputStream;\n\npublic class Test {\n\n public static void main(String[] args){\n File f = new File(\"D:/LOL.exe\");\n new FileInputStream(f);\n }\n \n}\n\n\n/*输出:\nException in thread \"main\" java.lang.Error: Unresolved compilation problem:\n Unhandled exception type FileNotFoundException\n\n at ExceptionProcess.Test.main(Test.java:10)\n*/\n```\n\n\n\n## 异常处理\n\n异常处理常见手段: try catch finally throws\n\n### try catch\n\n1. 将可能抛出FileNotFoundException **文件不存在异常**的代码放在try里\n\n2. 如果文件存在,就会顺序往下执行,并且不执行catch块中的代码\n\n3. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中\n\n4. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n\n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 使用异常的父类进行catch\n\n`FileNotFoundException`是`Exception`的子类,使用Exception也可以catch住 FileNotFoundException\n\n```java\nFile f= new File(\"d:/LOL.exe\");\n\ntry{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n}\n\ncatch(Exception e){ \n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法一\n\n有的时候一段代码会抛出多种异常,比如\n\n```java\nnew FileInputStream(f);\nDate d = sdf.parse(\"2016-06-03\");\n```\n\n这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException\n解决办法之一是**分别进行catch**\n\n```java\ncatch (FileNotFoundException e) {\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n \n} catch (ParseException e) {\n System.out.println(\"日期格式解析错误\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法2\n\n另一个种办法是把多个异常,放在一个catch里统一捕捉\n\n```java\ncatch (FileNotFoundException | ParseException e) {\n \n}\n```\n\n这种方式从 JDK7开始支持,好处是捕捉的代码**更紧凑**,不足之处是,一旦发生异常,**不能确定到底是哪种异常**,需要通过`instanceof` 进行判断具体的异常类型\n\n```java\nif (e instanceof FileNotFoundException)\n\tSystem.out.println(\"d:/LOL.exe不存在\");\nif (e instanceof ParseException)\n\tSystem.out.println(\"日期格式解析错误\");\n```\n\n\n\n例如:\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n Date d = sdf.parse(\"2016-06-03\");\n \n } catch (FileNotFoundException | ParseException e) {\n if (e instanceof FileNotFoundException)\n System.out.println(\"d:/LOL.exe不存在\");\n if (e instanceof ParseException)\n System.out.println(\"日期格式解析错误\");\n \n e.printStackTrace();\n }\n \n }\n}\n```\n\n### finally\n\n无论是否出现异常,finally中的代码都会被执行\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f= new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n finally{\n System.out.println(\"无论文件是否存在, 都会执行的代码\");\n }\n }\n}\n```\n\n\n\n### throws\n\n> 考虑如下情况:\n>\n> 1. 主方法调用method1\n> 2. method1调用method2\n> 3. method2中打开文件\n> 4. method2中需要进行异常处理\n> 但是method2**不打算处理**,而是把这个异常通过**throws**抛出去\n> 5. 那么method1就会**接到该异常**。 处理办法也是两种,要么是try catch处理掉,要么也是**抛出去**。\n> 6. method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n method1();\n \n }\n \n private static void method1() {\n try {\n method2();\n } catch (FileNotFoundException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n \n private static void method2() throws FileNotFoundException {\n \n File f = new File(\"d:/LOL.exe\");\n \n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n \n }\n}\n```\n\n\n\n### throws与throw区别\n\nthrows与throw这两个关键字接近,不过意义不一样,有如下区别:\n\n1. throws 出现在方法**声明**上,而throw通常都出现在**方法体内**。\n\n2. - throws 表示出现异常的一种可能性,并不一定会发生这些异常;\n\n - throw则是抛出了异常,执行throw则一定抛出了某个异常对象\n\n## 异常分类\n\n- 可查异常(CheckedException)\n- 非可查异常\n - 运行时异常(RunTimeException)\n - 错误(Error)![三种分类](https://stepimagewm.how2j.cn/2412.png)\n\n\n\n### 可查异常\n\n可查异常即**必须进行处理的异常**,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException\n如果不处理,编译器,就不让你通过\n\n\n\n### 运行时异常\n\n运行时异常RuntimeException指: **不是必须进行try catch的异常**\n\n**常见运行时异常:**\n\n- 除数不能为0异常:ArithmeticException\n- 下标越界异常:ArrayIndexOutOfBoundsException\n- 空指针异常:NullPointerException\n\n在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,**即便不进行try catch,也不会有编译错误**。\n\nJava之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常**太过于普遍**,如果都需要进行捕捉,代码的可读性就会变得很糟糕。\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n //任何除数不能为0:ArithmeticException\n int k = 5/0;\n \n //下标越界异常:ArrayIndexOutOfBoundsException\n int j[] = new int[5];\n j[10] = 10;\n \n //空指针异常:NullPointerException\n String str = null;\n str.length();\n }\n}\n```\n\n### 错误Error\n\n指的是**系统级别的异常**,通常是内存用光了\n\n在**默认设置下**,一般java程序启动的时候,最大可以使用16m的内存\n\n如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出`OutOfMemoryError`,与运行时异常一样,错误也是不要求强制捕捉的\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n StringBuffer sb =new StringBuffer();\n \n for (int i = 0; i < Integer.MAX_VALUE; i++) {\n sb.append('a');\n }\n \n }\n \n}\n```\n\n\n\n## Throwable\n\n### Throwable\n\nThrowable是类,Exception和Error都继承了该类\n所以在捕捉的时候,也可以使用Throwable进行捕捉\n如图: 异常分**Error**和**Exception**\nException里又分**运行时异常**和**可查异常**\n\n![Throwable](https://stepimagewm.how2j.cn/742.png)\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n new FileInputStream(f);\n //使用Throwable进行异常捕捉\n } catch (Throwable t) {\n // TODO Auto-generated catch block\n t.printStackTrace();\n }\n \n }\n}\n```\n\n## 自定义异常\n\n### 创建自定义异常\n\n一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException\n创建一个类EnemyHeroIsDeadException,并继承Exception\n提供两个构造方法\n\n1. 无参的构造方法\n2. 带参的构造方法,并调用父类的对应的构造方法\n\n```java\nclass EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n}\n```\n\n\n\n### 抛出自定义异常\n\n在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常\n\n1. 创建一个EnemyHeroIsDeadException实例\n\n2. 通过**throw** 抛出该异常\n\n3. 当前方法通过 **throws** 抛出该异常\n\n在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void attackHero(Hero h) throws EnemyHeroIsDeadException{\n if(h.hp == 0){\n throw new EnemyHeroIsDeadException(h.name + \" 已经挂了,不需要施放技能\" );\n }\n }\n \n public String toString(){\n return name;\n }\n \n class EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n garen.hp = 616;\n \n Hero teemo = new Hero();\n teemo.name = \"提莫\";\n teemo.hp = 0;\n \n try {\n garen.attackHero(teemo);\n \n } catch (EnemyHeroIsDeadException e) {\n // TODO Auto-generated catch block\n System.out.println(\"异常的具体原因:\"+e.getMessage());\n e.printStackTrace();\n }\n \n }\n}\n```\n\n输出:\n\n![抛出自定义异常](https://stepimagewm.how2j.cn/744.png)","source":"_posts/javaSE/15-异常处理.md","raw":"---\ntitle: 15-异常处理\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: f5c95c\ndate: 2021-02-15 10:46:30\ndescription: java中的异常处理\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 异常处理\n\n## 异常定义\n\n导致程序的正常流程被中断的事件,叫做异常\n\n异常处理常见手段:try catch finally throws\n\n\n\n例如:文件不存在异常\n\n```java\npackage ExceptionProcess;\n\nimport java.io.File;\nimport java.io.FileInputStream;\n\npublic class Test {\n\n public static void main(String[] args){\n File f = new File(\"D:/LOL.exe\");\n new FileInputStream(f);\n }\n \n}\n\n\n/*输出:\nException in thread \"main\" java.lang.Error: Unresolved compilation problem:\n Unhandled exception type FileNotFoundException\n\n at ExceptionProcess.Test.main(Test.java:10)\n*/\n```\n\n\n\n## 异常处理\n\n异常处理常见手段: try catch finally throws\n\n### try catch\n\n1. 将可能抛出FileNotFoundException **文件不存在异常**的代码放在try里\n\n2. 如果文件存在,就会顺序往下执行,并且不执行catch块中的代码\n\n3. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中\n\n4. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n\n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 使用异常的父类进行catch\n\n`FileNotFoundException`是`Exception`的子类,使用Exception也可以catch住 FileNotFoundException\n\n```java\nFile f= new File(\"d:/LOL.exe\");\n\ntry{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n}\n\ncatch(Exception e){ \n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法一\n\n有的时候一段代码会抛出多种异常,比如\n\n```java\nnew FileInputStream(f);\nDate d = sdf.parse(\"2016-06-03\");\n```\n\n这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException\n解决办法之一是**分别进行catch**\n\n```java\ncatch (FileNotFoundException e) {\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n \n} catch (ParseException e) {\n System.out.println(\"日期格式解析错误\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法2\n\n另一个种办法是把多个异常,放在一个catch里统一捕捉\n\n```java\ncatch (FileNotFoundException | ParseException e) {\n \n}\n```\n\n这种方式从 JDK7开始支持,好处是捕捉的代码**更紧凑**,不足之处是,一旦发生异常,**不能确定到底是哪种异常**,需要通过`instanceof` 进行判断具体的异常类型\n\n```java\nif (e instanceof FileNotFoundException)\n\tSystem.out.println(\"d:/LOL.exe不存在\");\nif (e instanceof ParseException)\n\tSystem.out.println(\"日期格式解析错误\");\n```\n\n\n\n例如:\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n Date d = sdf.parse(\"2016-06-03\");\n \n } catch (FileNotFoundException | ParseException e) {\n if (e instanceof FileNotFoundException)\n System.out.println(\"d:/LOL.exe不存在\");\n if (e instanceof ParseException)\n System.out.println(\"日期格式解析错误\");\n \n e.printStackTrace();\n }\n \n }\n}\n```\n\n### finally\n\n无论是否出现异常,finally中的代码都会被执行\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f= new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n finally{\n System.out.println(\"无论文件是否存在, 都会执行的代码\");\n }\n }\n}\n```\n\n\n\n### throws\n\n> 考虑如下情况:\n>\n> 1. 主方法调用method1\n> 2. method1调用method2\n> 3. method2中打开文件\n> 4. method2中需要进行异常处理\n> 但是method2**不打算处理**,而是把这个异常通过**throws**抛出去\n> 5. 那么method1就会**接到该异常**。 处理办法也是两种,要么是try catch处理掉,要么也是**抛出去**。\n> 6. method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n method1();\n \n }\n \n private static void method1() {\n try {\n method2();\n } catch (FileNotFoundException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n \n private static void method2() throws FileNotFoundException {\n \n File f = new File(\"d:/LOL.exe\");\n \n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n \n }\n}\n```\n\n\n\n### throws与throw区别\n\nthrows与throw这两个关键字接近,不过意义不一样,有如下区别:\n\n1. throws 出现在方法**声明**上,而throw通常都出现在**方法体内**。\n\n2. - throws 表示出现异常的一种可能性,并不一定会发生这些异常;\n\n - throw则是抛出了异常,执行throw则一定抛出了某个异常对象\n\n## 异常分类\n\n- 可查异常(CheckedException)\n- 非可查异常\n - 运行时异常(RunTimeException)\n - 错误(Error)![三种分类](https://stepimagewm.how2j.cn/2412.png)\n\n\n\n### 可查异常\n\n可查异常即**必须进行处理的异常**,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException\n如果不处理,编译器,就不让你通过\n\n\n\n### 运行时异常\n\n运行时异常RuntimeException指: **不是必须进行try catch的异常**\n\n**常见运行时异常:**\n\n- 除数不能为0异常:ArithmeticException\n- 下标越界异常:ArrayIndexOutOfBoundsException\n- 空指针异常:NullPointerException\n\n在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,**即便不进行try catch,也不会有编译错误**。\n\nJava之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常**太过于普遍**,如果都需要进行捕捉,代码的可读性就会变得很糟糕。\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n //任何除数不能为0:ArithmeticException\n int k = 5/0;\n \n //下标越界异常:ArrayIndexOutOfBoundsException\n int j[] = new int[5];\n j[10] = 10;\n \n //空指针异常:NullPointerException\n String str = null;\n str.length();\n }\n}\n```\n\n### 错误Error\n\n指的是**系统级别的异常**,通常是内存用光了\n\n在**默认设置下**,一般java程序启动的时候,最大可以使用16m的内存\n\n如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出`OutOfMemoryError`,与运行时异常一样,错误也是不要求强制捕捉的\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n StringBuffer sb =new StringBuffer();\n \n for (int i = 0; i < Integer.MAX_VALUE; i++) {\n sb.append('a');\n }\n \n }\n \n}\n```\n\n\n\n## Throwable\n\n### Throwable\n\nThrowable是类,Exception和Error都继承了该类\n所以在捕捉的时候,也可以使用Throwable进行捕捉\n如图: 异常分**Error**和**Exception**\nException里又分**运行时异常**和**可查异常**\n\n![Throwable](https://stepimagewm.how2j.cn/742.png)\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n new FileInputStream(f);\n //使用Throwable进行异常捕捉\n } catch (Throwable t) {\n // TODO Auto-generated catch block\n t.printStackTrace();\n }\n \n }\n}\n```\n\n## 自定义异常\n\n### 创建自定义异常\n\n一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException\n创建一个类EnemyHeroIsDeadException,并继承Exception\n提供两个构造方法\n\n1. 无参的构造方法\n2. 带参的构造方法,并调用父类的对应的构造方法\n\n```java\nclass EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n}\n```\n\n\n\n### 抛出自定义异常\n\n在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常\n\n1. 创建一个EnemyHeroIsDeadException实例\n\n2. 通过**throw** 抛出该异常\n\n3. 当前方法通过 **throws** 抛出该异常\n\n在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void attackHero(Hero h) throws EnemyHeroIsDeadException{\n if(h.hp == 0){\n throw new EnemyHeroIsDeadException(h.name + \" 已经挂了,不需要施放技能\" );\n }\n }\n \n public String toString(){\n return name;\n }\n \n class EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n garen.hp = 616;\n \n Hero teemo = new Hero();\n teemo.name = \"提莫\";\n teemo.hp = 0;\n \n try {\n garen.attackHero(teemo);\n \n } catch (EnemyHeroIsDeadException e) {\n // TODO Auto-generated catch block\n System.out.println(\"异常的具体原因:\"+e.getMessage());\n e.printStackTrace();\n }\n \n }\n}\n```\n\n输出:\n\n![抛出自定义异常](https://stepimagewm.how2j.cn/744.png)","slug":"javaSE/15-异常处理","published":1,"updated":"2021-08-29T15:31:38.885Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qn005takveawfj22pb","content":"

异常处理

异常定义

导致程序的正常流程被中断的事件,叫做异常

\n

异常处理常见手段:try catch finally throws

\n

例如:文件不存在异常

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package ExceptionProcess;

import java.io.File;
import java.io.FileInputStream;

public class Test {

public static void main(String[] args){
File f = new File("D:/LOL.exe");
new FileInputStream(f);
}

}


/*输出:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unhandled exception type FileNotFoundException

at ExceptionProcess.Test.main(Test.java:10)
*/
\n

异常处理

异常处理常见手段: try catch finally throws

\n

try catch

    \n
  1. 将可能抛出FileNotFoundException 文件不存在异常的代码放在try里

    \n
  2. \n
  3. 如果文件存在,就会顺序往下执行,并且不执行catch块中的代码

    \n
  4. \n
  5. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中

    \n
  6. \n
  7. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常

    \n
  8. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}
catch(FileNotFoundException e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}

}
}
\n

使用异常的父类进行catch

FileNotFoundExceptionException的子类,使用Exception也可以catch住 FileNotFoundException

\n
1
2
3
4
5
6
7
8
9
10
11
12
File f= new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}

catch(Exception e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}
\n

多异常捕捉办法一

有的时候一段代码会抛出多种异常,比如

\n
1
2
new FileInputStream(f);
Date d = sdf.parse("2016-06-03");
\n

这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException
解决办法之一是分别进行catch

\n
1
2
3
4
5
6
7
8
catch (FileNotFoundException e) {
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();

} catch (ParseException e) {
System.out.println("日期格式解析错误");
e.printStackTrace();
}
\n

多异常捕捉办法2

另一个种办法是把多个异常,放在一个catch里统一捕捉

\n
1
2
3
catch (FileNotFoundException | ParseException e) {

}
\n

这种方式从 JDK7开始支持,好处是捕捉的代码更紧凑,不足之处是,一旦发生异常,不能确定到底是哪种异常,需要通过instanceof 进行判断具体的异常类型

\n
1
2
3
4
if (e instanceof FileNotFoundException)
\tSystem.out.println("d:/LOL.exe不存在");
if (e instanceof ParseException)
\tSystem.out.println("日期格式解析错误");
\n

例如:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try {
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse("2016-06-03");

} catch (FileNotFoundException | ParseException e) {
if (e instanceof FileNotFoundException)
System.out.println("d:/LOL.exe不存在");
if (e instanceof ParseException)
System.out.println("日期格式解析错误");

e.printStackTrace();
}

}
}
\n

finally

无论是否出现异常,finally中的代码都会被执行

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {

File f= new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}
catch(FileNotFoundException e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}
finally{
System.out.println("无论文件是否存在, 都会执行的代码");
}
}
}
\n

throws

\n

考虑如下情况:

\n
    \n
  1. 主方法调用method1
  2. \n
  3. method1调用method2
  4. \n
  5. method2中打开文件
  6. \n
  7. method2中需要进行异常处理
    但是method2不打算处理,而是把这个异常通过throws抛出去
  8. \n
  9. 那么method1就会接到该异常。 处理办法也是两种,要么是try catch处理掉,要么也是抛出去
  10. \n
  11. method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了
  12. \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {
method1();

}

private static void method1() {
try {
method2();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

private static void method2() throws FileNotFoundException {

File f = new File("d:/LOL.exe");

System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");

}
}
\n

throws与throw区别

throws与throw这两个关键字接近,不过意义不一样,有如下区别:

\n
    \n
  1. throws 出现在方法声明上,而throw通常都出现在方法体内

    \n
  2. \n
    • \n
    • throws 表示出现异常的一种可能性,并不一定会发生这些异常;

      \n
    • \n
    • throw则是抛出了异常,执行throw则一定抛出了某个异常对象

      \n
    • \n
    \n
  3. \n
\n

异常分类

    \n
  • 可查异常(CheckedException)
  • \n
  • 非可查异常
      \n
    • 运行时异常(RunTimeException)
    • \n
    • 错误(Error)\"三种分类\"
    • \n
    \n
  • \n
\n

可查异常

可查异常即必须进行处理的异常,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException
如果不处理,编译器,就不让你通过

\n

运行时异常

运行时异常RuntimeException指: 不是必须进行try catch的异常

\n

常见运行时异常:

\n
    \n
  • 除数不能为0异常:ArithmeticException
  • \n
  • 下标越界异常:ArrayIndexOutOfBoundsException
  • \n
  • 空指针异常:NullPointerException
  • \n
\n

在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,即便不进行try catch,也不会有编译错误

\n

Java之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常太过于普遍,如果都需要进行捕捉,代码的可读性就会变得很糟糕。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package exception;

public class TestException {

public static void main(String[] args) {

//任何除数不能为0:ArithmeticException
int k = 5/0;

//下标越界异常:ArrayIndexOutOfBoundsException
int j[] = new int[5];
j[10] = 10;

//空指针异常:NullPointerException
String str = null;
str.length();
}
}
\n

错误Error

指的是系统级别的异常,通常是内存用光了

\n

默认设置下,一般java程序启动的时候,最大可以使用16m的内存

\n

如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出OutOfMemoryError,与运行时异常一样,错误也是不要求强制捕捉的

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package exception;

public class TestException {

public static void main(String[] args) {

StringBuffer sb =new StringBuffer();

for (int i = 0; i < Integer.MAX_VALUE; i++) {
sb.append('a');
}

}

}
\n

Throwable

Throwable

Throwable是类,Exception和Error都继承了该类
所以在捕捉的时候,也可以使用Throwable进行捕捉
如图: 异常分ErrorException
Exception里又分运行时异常可查异常

\n

\"Throwable\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package exception;

import java.io.File;
import java.io.FileInputStream;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try {
new FileInputStream(f);
//使用Throwable进行异常捕捉
} catch (Throwable t) {
// TODO Auto-generated catch block
t.printStackTrace();
}

}
}
\n

自定义异常

创建自定义异常

一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException
创建一个类EnemyHeroIsDeadException,并继承Exception
提供两个构造方法

\n
    \n
  1. 无参的构造方法
  2. \n
  3. 带参的构造方法,并调用父类的对应的构造方法
  4. \n
\n
1
2
3
4
5
6
7
8
9
class EnemyHeroIsDeadException extends Exception{

public EnemyHeroIsDeadException(){

}
public EnemyHeroIsDeadException(String msg){
super(msg);
}
}
\n

抛出自定义异常

在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常

\n
    \n
  1. 创建一个EnemyHeroIsDeadException实例

    \n
  2. \n
  3. 通过throw 抛出该异常

    \n
  4. \n
  5. 当前方法通过 throws 抛出该异常

    \n
  6. \n
\n

在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package charactor;

public class Hero {
public String name;
protected float hp;

public void attackHero(Hero h) throws EnemyHeroIsDeadException{
if(h.hp == 0){
throw new EnemyHeroIsDeadException(h.name + " 已经挂了,不需要施放技能" );
}
}

public String toString(){
return name;
}

class EnemyHeroIsDeadException extends Exception{

public EnemyHeroIsDeadException(){

}
public EnemyHeroIsDeadException(String msg){
super(msg);
}
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";
garen.hp = 616;

Hero teemo = new Hero();
teemo.name = "提莫";
teemo.hp = 0;

try {
garen.attackHero(teemo);

} catch (EnemyHeroIsDeadException e) {
// TODO Auto-generated catch block
System.out.println("异常的具体原因:"+e.getMessage());
e.printStackTrace();
}

}
}
\n

输出:

\n

\"抛出自定义异常\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

异常处理

异常定义

导致程序的正常流程被中断的事件,叫做异常

\n

异常处理常见手段:try catch finally throws

\n

例如:文件不存在异常

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package ExceptionProcess;

import java.io.File;
import java.io.FileInputStream;

public class Test {

public static void main(String[] args){
File f = new File("D:/LOL.exe");
new FileInputStream(f);
}

}


/*输出:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unhandled exception type FileNotFoundException

at ExceptionProcess.Test.main(Test.java:10)
*/
\n

异常处理

异常处理常见手段: try catch finally throws

\n

try catch

    \n
  1. 将可能抛出FileNotFoundException 文件不存在异常的代码放在try里

    \n
  2. \n
  3. 如果文件存在,就会顺序往下执行,并且不执行catch块中的代码

    \n
  4. \n
  5. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中

    \n
  6. \n
  7. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常

    \n
  8. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}
catch(FileNotFoundException e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}

}
}
\n

使用异常的父类进行catch

FileNotFoundExceptionException的子类,使用Exception也可以catch住 FileNotFoundException

\n
1
2
3
4
5
6
7
8
9
10
11
12
File f= new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}

catch(Exception e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}
\n

多异常捕捉办法一

有的时候一段代码会抛出多种异常,比如

\n
1
2
new FileInputStream(f);
Date d = sdf.parse("2016-06-03");
\n

这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException
解决办法之一是分别进行catch

\n
1
2
3
4
5
6
7
8
catch (FileNotFoundException e) {
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();

} catch (ParseException e) {
System.out.println("日期格式解析错误");
e.printStackTrace();
}
\n

多异常捕捉办法2

另一个种办法是把多个异常,放在一个catch里统一捕捉

\n
1
2
3
catch (FileNotFoundException | ParseException e) {

}
\n

这种方式从 JDK7开始支持,好处是捕捉的代码更紧凑,不足之处是,一旦发生异常,不能确定到底是哪种异常,需要通过instanceof 进行判断具体的异常类型

\n
1
2
3
4
if (e instanceof FileNotFoundException)
\tSystem.out.println("d:/LOL.exe不存在");
if (e instanceof ParseException)
\tSystem.out.println("日期格式解析错误");
\n

例如:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try {
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse("2016-06-03");

} catch (FileNotFoundException | ParseException e) {
if (e instanceof FileNotFoundException)
System.out.println("d:/LOL.exe不存在");
if (e instanceof ParseException)
System.out.println("日期格式解析错误");

e.printStackTrace();
}

}
}
\n

finally

无论是否出现异常,finally中的代码都会被执行

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {

File f= new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}
catch(FileNotFoundException e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}
finally{
System.out.println("无论文件是否存在, 都会执行的代码");
}
}
}
\n

throws

\n

考虑如下情况:

\n
    \n
  1. 主方法调用method1
  2. \n
  3. method1调用method2
  4. \n
  5. method2中打开文件
  6. \n
  7. method2中需要进行异常处理
    但是method2不打算处理,而是把这个异常通过throws抛出去
  8. \n
  9. 那么method1就会接到该异常。 处理办法也是两种,要么是try catch处理掉,要么也是抛出去
  10. \n
  11. method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了
  12. \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {
method1();

}

private static void method1() {
try {
method2();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

private static void method2() throws FileNotFoundException {

File f = new File("d:/LOL.exe");

System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");

}
}
\n

throws与throw区别

throws与throw这两个关键字接近,不过意义不一样,有如下区别:

\n
    \n
  1. throws 出现在方法声明上,而throw通常都出现在方法体内

    \n
  2. \n
    • \n
    • throws 表示出现异常的一种可能性,并不一定会发生这些异常;

      \n
    • \n
    • throw则是抛出了异常,执行throw则一定抛出了某个异常对象

      \n
    • \n
    \n
  3. \n
\n

异常分类

    \n
  • 可查异常(CheckedException)
  • \n
  • 非可查异常
      \n
    • 运行时异常(RunTimeException)
    • \n
    • 错误(Error)\"三种分类\"
    • \n
    \n
  • \n
\n

可查异常

可查异常即必须进行处理的异常,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException
如果不处理,编译器,就不让你通过

\n

运行时异常

运行时异常RuntimeException指: 不是必须进行try catch的异常

\n

常见运行时异常:

\n
    \n
  • 除数不能为0异常:ArithmeticException
  • \n
  • 下标越界异常:ArrayIndexOutOfBoundsException
  • \n
  • 空指针异常:NullPointerException
  • \n
\n

在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,即便不进行try catch,也不会有编译错误

\n

Java之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常太过于普遍,如果都需要进行捕捉,代码的可读性就会变得很糟糕。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package exception;

public class TestException {

public static void main(String[] args) {

//任何除数不能为0:ArithmeticException
int k = 5/0;

//下标越界异常:ArrayIndexOutOfBoundsException
int j[] = new int[5];
j[10] = 10;

//空指针异常:NullPointerException
String str = null;
str.length();
}
}
\n

错误Error

指的是系统级别的异常,通常是内存用光了

\n

默认设置下,一般java程序启动的时候,最大可以使用16m的内存

\n

如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出OutOfMemoryError,与运行时异常一样,错误也是不要求强制捕捉的

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package exception;

public class TestException {

public static void main(String[] args) {

StringBuffer sb =new StringBuffer();

for (int i = 0; i < Integer.MAX_VALUE; i++) {
sb.append('a');
}

}

}
\n

Throwable

Throwable

Throwable是类,Exception和Error都继承了该类
所以在捕捉的时候,也可以使用Throwable进行捕捉
如图: 异常分ErrorException
Exception里又分运行时异常可查异常

\n

\"Throwable\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package exception;

import java.io.File;
import java.io.FileInputStream;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try {
new FileInputStream(f);
//使用Throwable进行异常捕捉
} catch (Throwable t) {
// TODO Auto-generated catch block
t.printStackTrace();
}

}
}
\n

自定义异常

创建自定义异常

一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException
创建一个类EnemyHeroIsDeadException,并继承Exception
提供两个构造方法

\n
    \n
  1. 无参的构造方法
  2. \n
  3. 带参的构造方法,并调用父类的对应的构造方法
  4. \n
\n
1
2
3
4
5
6
7
8
9
class EnemyHeroIsDeadException extends Exception{

public EnemyHeroIsDeadException(){

}
public EnemyHeroIsDeadException(String msg){
super(msg);
}
}
\n

抛出自定义异常

在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常

\n
    \n
  1. 创建一个EnemyHeroIsDeadException实例

    \n
  2. \n
  3. 通过throw 抛出该异常

    \n
  4. \n
  5. 当前方法通过 throws 抛出该异常

    \n
  6. \n
\n

在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package charactor;

public class Hero {
public String name;
protected float hp;

public void attackHero(Hero h) throws EnemyHeroIsDeadException{
if(h.hp == 0){
throw new EnemyHeroIsDeadException(h.name + " 已经挂了,不需要施放技能" );
}
}

public String toString(){
return name;
}

class EnemyHeroIsDeadException extends Exception{

public EnemyHeroIsDeadException(){

}
public EnemyHeroIsDeadException(String msg){
super(msg);
}
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";
garen.hp = 616;

Hero teemo = new Hero();
teemo.name = "提莫";
teemo.hp = 0;

try {
garen.attackHero(teemo);

} catch (EnemyHeroIsDeadException e) {
// TODO Auto-generated catch block
System.out.println("异常的具体原因:"+e.getMessage());
e.printStackTrace();
}

}
}
\n

输出:

\n

\"抛出自定义异常\"

\n"},{"title":"12-字符串操作","abbrlink":"5e64b19a","date":"2021-02-13T11:20:44.000Z","description":"java的字符串初始化、基本操作函数的使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 12 java字符串\n\nJava用`String` 和 `StringBuffer`类来处理字符串,在Java中,每个字符串都是一个对象。\n\n- String:主要用于内容不可改变的字符串对象,即字符串一旦创建就不能再改变了(只读)\n- StringBuffer:用于串内容可以改变的字符串对象(可读、可写)\n\n## 12.1字符串初始化\n\n以下用3种方法创建并初始化字符串对象\n\n```java\nString s1 = new String( \"hello\");\n\nString s2 = \"hello\";\n\nchar[] ch = {'h', 'e', 'l', 'l', 'o'};\nString s3 = new String(ch);\n\n```\n\n## 12.2 字符串连接\n\n字符串连接用 `+` 号\n\n```java\nString str1 = \"abc\";\nString str2 = \"123\";\n\nString str3 = str1 + str2; //字符串连接\n```\n\n\n\n## 12.3 字符串比较\n\n1. `==`只检查两个串**引用**是否相同,不比较串值;\n\n > 注意:\n >\n > 一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象\n > 所以在如下代码中:\n >\n > 第6行会创建了一个新的字符串\"the light\",\n >\n > 但是在第7行,编译器发现已经存在现成的\"the light\",那么就直接拿来使用,而没有进行重复创建\n\n ```java\n package character;\n \n public class TestString {\n \n public static void main(String[] args) {\n String str1 = \"the light\";\n String str3 = \"the light\";\n System.out.println( str1 == str3); //true\n }\n \n }\n ```\n\n \n\n2. `equals()`或`equalsIgnoreCase()`方法:比较串值是否相同\n\n `equalsIgnoreCase()`表示忽略大小写的影响\n\n \n\n3. `compareTo()`方法:串大小比较(字典序)\n\n\n\n```java\nstring str1 = \"abc\"; \nstring str2 = \"a\";\n\nstr2 += \"bc\"; \nif(str1 == str2){ }; //false\nif(str1.equals(str2)){ }; \t\t//true\n\n```\n\n\n\n## 12.4 字符串拆分\n\n`split(regex)`方法根据匹配确定的分隔符 `regex` 将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;\n\n```java\npublic class HelloWorld {\n public static void main(String[] args){\n\n String s = \"The-cat-sat-on-the-mat.\";\n \n String[] words = s.split(\"-\"); //从-处开始分割\n \n for(int i=0; i 注意:\n >\n > 一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象\n > 所以在如下代码中:\n >\n > 第6行会创建了一个新的字符串\"the light\",\n >\n > 但是在第7行,编译器发现已经存在现成的\"the light\",那么就直接拿来使用,而没有进行重复创建\n\n ```java\n package character;\n \n public class TestString {\n \n public static void main(String[] args) {\n String str1 = \"the light\";\n String str3 = \"the light\";\n System.out.println( str1 == str3); //true\n }\n \n }\n ```\n\n \n\n2. `equals()`或`equalsIgnoreCase()`方法:比较串值是否相同\n\n `equalsIgnoreCase()`表示忽略大小写的影响\n\n \n\n3. `compareTo()`方法:串大小比较(字典序)\n\n\n\n```java\nstring str1 = \"abc\"; \nstring str2 = \"a\";\n\nstr2 += \"bc\"; \nif(str1 == str2){ }; //false\nif(str1.equals(str2)){ }; \t\t//true\n\n```\n\n\n\n## 12.4 字符串拆分\n\n`split(regex)`方法根据匹配确定的分隔符 `regex` 将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;\n\n```java\npublic class HelloWorld {\n public static void main(String[] args){\n\n String s = \"The-cat-sat-on-the-mat.\";\n \n String[] words = s.split(\"-\"); //从-处开始分割\n \n for(int i=0; i12 java字符串

Java用StringStringBuffer类来处理字符串,在Java中,每个字符串都是一个对象。

\n
    \n
  • String:主要用于内容不可改变的字符串对象,即字符串一旦创建就不能再改变了(只读)
  • \n
  • StringBuffer:用于串内容可以改变的字符串对象(可读、可写)
  • \n
\n

12.1字符串初始化

以下用3种方法创建并初始化字符串对象

\n
1
2
3
4
5
6
7
String  s1 = new String( "hello");

String s2 = "hello";

char[] ch = {'h', 'e', 'l', 'l', 'o'};
String s3 = new String(ch);

\n

12.2 字符串连接

字符串连接用 +

\n
1
2
3
4
String  str1 = "abc";
String str2 = "123";

String str3 = str1 + str2; //字符串连接
\n

12.3 字符串比较

    \n
  1. ==只检查两个串引用是否相同,不比较串值;

    \n
    \n

    注意:

    \n

    一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象
    所以在如下代码中:

    \n

    第6行会创建了一个新的字符串”the light”,

    \n

    但是在第7行,编译器发现已经存在现成的”the light”,那么就直接拿来使用,而没有进行重复创建

    \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package character;

    public class TestString {

    public static void main(String[] args) {
    String str1 = "the light";
    String str3 = "the light";
    System.out.println( str1 == str3); //true
    }

    }
    \n
  2. \n
\n
    \n
  1. equals()equalsIgnoreCase()方法:比较串值是否相同

    \n

    equalsIgnoreCase()表示忽略大小写的影响

    \n
  2. \n
\n
    \n
  1. compareTo()方法:串大小比较(字典序)
  2. \n
\n
1
2
3
4
5
6
7
string  str1 = "abc";    
string str2 = "a";

str2 += "bc";
if(str1 == str2){ }; //false
if(str1.equals(str2)){ }; \t\t//true

\n

12.4 字符串拆分

split(regex)方法根据匹配确定的分隔符 regex 将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class HelloWorld {
public static void main(String[] args){

String s = "The-cat-sat-on-the-mat.";

String[] words = s.split("-"); //从-处开始分割

for(int i=0; i<words.length; i++)
{
System.out.println(words[i]);
}

}
}

/*输出:
The
cat
sat
on
the
mat.
*/
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloWorld {
public static void main(String[] args){

String s = "12+34+567";
String[] ss = s.split("[+]"); //注意中括号[]不能省略

int sum = 0;
for(int i=0;i<ss.length;i++)
{
sum += Integer.parseInt(ss[i]);
}
System.out.println(sum);

}
}

//输出:613
\n

12.5操作字符串常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int length();\t\t\t//返回字符串长度

char charAt(int index); //返回指定下标的字符

boolean equals(String s); //判断当前字符串和s串相等

int indexOf(String str); //返回str在串中首次出现位置

String concat(String str); //把str连接在当前串之后。

String substring(int begin ,int end); //截取子串[begin,end-1]

String toLowerCase(); //将串转换成小写并返回

String toUpperCase(); //将串转换成大写并返回

String trim(); //将串开始和结尾的空串去掉。

char[] toCharArray(); //返回对应的字符数组

String replace(char old, char new); //新字符替代旧字符

String replaceAll(String old, String new) // 用 `new`串替代字符串中【所有的】 `old`串

String replaceFirst(String old, String new) //只替代【第一个】出现的
\n

12.6 StringBuffer类

StringBuffer类创建的串其内容是可以修改的,其占用的空间能自动增长:

\n
1
String s = "a" + 4 + "b123" + false; \t//低效率
\n

提高效率被编译成下列等价代码:

\n
1
2
StringBuffer s = new StringBuffer(); 
s.append("a").append(4).append("b123").append(false).toString();
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

12 java字符串

Java用StringStringBuffer类来处理字符串,在Java中,每个字符串都是一个对象。

\n
    \n
  • String:主要用于内容不可改变的字符串对象,即字符串一旦创建就不能再改变了(只读)
  • \n
  • StringBuffer:用于串内容可以改变的字符串对象(可读、可写)
  • \n
\n

12.1字符串初始化

以下用3种方法创建并初始化字符串对象

\n
1
2
3
4
5
6
7
String  s1 = new String( "hello");

String s2 = "hello";

char[] ch = {'h', 'e', 'l', 'l', 'o'};
String s3 = new String(ch);

\n

12.2 字符串连接

字符串连接用 +

\n
1
2
3
4
String  str1 = "abc";
String str2 = "123";

String str3 = str1 + str2; //字符串连接
\n

12.3 字符串比较

    \n
  1. ==只检查两个串引用是否相同,不比较串值;

    \n
    \n

    注意:

    \n

    一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象
    所以在如下代码中:

    \n

    第6行会创建了一个新的字符串”the light”,

    \n

    但是在第7行,编译器发现已经存在现成的”the light”,那么就直接拿来使用,而没有进行重复创建

    \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package character;

    public class TestString {

    public static void main(String[] args) {
    String str1 = "the light";
    String str3 = "the light";
    System.out.println( str1 == str3); //true
    }

    }
    \n
  2. \n
\n
    \n
  1. equals()equalsIgnoreCase()方法:比较串值是否相同

    \n

    equalsIgnoreCase()表示忽略大小写的影响

    \n
  2. \n
\n
    \n
  1. compareTo()方法:串大小比较(字典序)
  2. \n
\n
1
2
3
4
5
6
7
string  str1 = "abc";    
string str2 = "a";

str2 += "bc";
if(str1 == str2){ }; //false
if(str1.equals(str2)){ }; \t\t//true

\n

12.4 字符串拆分

split(regex)方法根据匹配确定的分隔符 regex 将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class HelloWorld {
public static void main(String[] args){

String s = "The-cat-sat-on-the-mat.";

String[] words = s.split("-"); //从-处开始分割

for(int i=0; i<words.length; i++)
{
System.out.println(words[i]);
}

}
}

/*输出:
The
cat
sat
on
the
mat.
*/
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloWorld {
public static void main(String[] args){

String s = "12+34+567";
String[] ss = s.split("[+]"); //注意中括号[]不能省略

int sum = 0;
for(int i=0;i<ss.length;i++)
{
sum += Integer.parseInt(ss[i]);
}
System.out.println(sum);

}
}

//输出:613
\n

12.5操作字符串常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int length();\t\t\t//返回字符串长度

char charAt(int index); //返回指定下标的字符

boolean equals(String s); //判断当前字符串和s串相等

int indexOf(String str); //返回str在串中首次出现位置

String concat(String str); //把str连接在当前串之后。

String substring(int begin ,int end); //截取子串[begin,end-1]

String toLowerCase(); //将串转换成小写并返回

String toUpperCase(); //将串转换成大写并返回

String trim(); //将串开始和结尾的空串去掉。

char[] toCharArray(); //返回对应的字符数组

String replace(char old, char new); //新字符替代旧字符

String replaceAll(String old, String new) // 用 `new`串替代字符串中【所有的】 `old`串

String replaceFirst(String old, String new) //只替代【第一个】出现的
\n

12.6 StringBuffer类

StringBuffer类创建的串其内容是可以修改的,其占用的空间能自动增长:

\n
1
String s = "a" + 4 + "b123" + false; \t//低效率
\n

提高效率被编译成下列等价代码:

\n
1
2
StringBuffer s = new StringBuffer(); 
s.append("a").append(4).append("b123").append(false).toString();
\n"},{"title":"13-日期与日历","abbrlink":"b3cec184","date":"2021-02-14T02:15:21.000Z","description":"java的日期与日历数据类型,如何使用及与字符串的转换","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 日期\n\n在Java中使用日期,需导入 `java.util.Date` 类\n\n```java\nimport java.util.Date;\n```\n\n\n\n## 创建一个日期对象\n\n- `Date date = new Date(); ` \n\n```java\nDate now = new Date(); \n\n//不加参数表示当前时间\n//加了参数n表示从1970-01-01 8:00:00开始经历了n毫秒\n```\n\n- 日期对象转字符串对象:\n\n```java\nString s = now.toString()\n```\n\n\n\n## getTime()方法\n\n`getTime()`是Date()对象的一个方法,返回类型 `long`,用于获取从1970-01-01 8:00:00开始到现在经历的**毫秒**数\n\n另外,`Date().getTime()` 和 `System.currentTimeMillis()` 是一样的\n\n```java\npackage date;\n \n//\nimport java.util.Date;\n \npublic class TestDate {\n \n public static void main(String[] args) {\n Date now= new Date();\n \n //当前日期的毫秒数\n System.out.println(\"Date.getTime() \\t\\t\\t返回值: \"+now.getTime());\n \n //通过System.currentTimeMillis()获取当前日期的毫秒数\n System.out.println(\"System.currentTimeMillis() \\t返回值: \"+System.currentTimeMillis());\n \n }\n}\n\n/*输出:\n\nDate.getTime() 返回值: 1611591854927\nSystem.currentTimeMillis() 返回值: 1611591854927\n\n*/\n\n```\n\n\n\n## 格式化日期并转为字符串\n\n格式化日期:\n\n需要用到 `SimpleDateFormat` 类\n\n```java\nimport java.text.SimpleDateFormat;\n```\n\n1. 指定格式\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss SSS\" );\n \n //y 代表年\n //M 代表月\n //d 代表日\n //H 代表24进制的小时\n //h 代表12进制的小时\n //m 代表分钟\n //s 代表秒\n //S 代表毫秒\n \n // 以上7种可以只选其中一种或多种,根据自己需要来,如:\n SimpleDateFormat sdf1 = new SimpleDateFormat(\"yyyy-MM-dd\" );\n SimpleDateFormat sdf2 = new SimpleDateFormat(\"MM/dd\" );\n ```\n\n2. 利用sdf对象的 `format`方法对日期 `Date()`进行格式化,并返回格式化后的`字符串`\n\n ```java\n Date d = new Date();\n String str = sdf.format(d); //这样日期就按格式转为了字符串\n ```\n\n\n\n## 字符串转日期\n\n三个步骤 :\n\n1. 指定字符串格式:\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\" );\n \n //SimpleDateFormat sdf1 =new SimpleDateFormat(\"yyyy/MM/dd\" );\n ```\n\n2. 创建字符串,注意格式要与 sdf 一致\n\n ```java\n String str = \"2019-1-15 16:02:27\";\n \n //String str1 = \"2019-1-15\";\n ```\n\n3. 创建日期对象,用 sdf的 `parse(str)`方法来把字符串转为日期对象\n\n ```java\n Date d = sdf.parse(str);\n ```\n\n\n\n## 日历 Calendar\n\n使用前需要导入:\n\n```java\nimport java.util.Calendar;\n```\n\n\n\n1. 获取日历对象 `Calendar.getInstance();`\n\n ```java\n Calendar cl = Calendar.getInstance();\n ```\n\n2. 通过日历对象得到日期对象: `getTime()`\n\n ```java\n Date d = cl.getTime();\n ```\n\n3. 日期设置:把 `cl` 这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 `setTime()`\n\n ```java\n Date d2 = new Date(0);\n cl.setTime(d2);\n ```\n\n## 翻日历\n\n> add方法,在原日期上**增加(add)** 年/月/日\n> set方法,**直接设置(set)** 年/月/日\n\n1. 创建日历对象\n\n ```java\n Calendar c = Calendar.getInstance();\n ```\n\n2. 获取当前日期\n\n ```java\n Date now = c.getTime();\n ```\n\n3. 把日历改成下个月的今天\n\n ```java\n c.setTime(now); //表示把日历改为当前时间\n \n c.add(Calendar.MONTH, 1); \n //在当前时间now的基础上修改日历,这里修改了月份,下个月 = 本月 + 1\n ```\n\n4. 把日历改成去年的今天\n\n ```java\n c.setTime(now);\n c.add(Calendar.YEAR, -1); //负数表示以前的,去年 = 今年 - 1\n ```\n\n5. 把日历直接**设置**成上个月的第三天\n\n ```java\n c.setTime(now);\n c.add(Calendar.MONTH, -1); //先改月份\n c.set(Calendar.DATE, 3);\t\t//再改日数,注意用set\n \n //如果是今天后的第三天,则用:\n //c.add(Calendar.DATE, 3)\n ```\n\n ","source":"_posts/javaSE/13-日期.md","raw":"---\ntitle: 13-日期与日历\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: b3cec184\ndate: 2021-02-14 10:15:21\ndescription: java的日期与日历数据类型,如何使用及与字符串的转换\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 日期\n\n在Java中使用日期,需导入 `java.util.Date` 类\n\n```java\nimport java.util.Date;\n```\n\n\n\n## 创建一个日期对象\n\n- `Date date = new Date(); ` \n\n```java\nDate now = new Date(); \n\n//不加参数表示当前时间\n//加了参数n表示从1970-01-01 8:00:00开始经历了n毫秒\n```\n\n- 日期对象转字符串对象:\n\n```java\nString s = now.toString()\n```\n\n\n\n## getTime()方法\n\n`getTime()`是Date()对象的一个方法,返回类型 `long`,用于获取从1970-01-01 8:00:00开始到现在经历的**毫秒**数\n\n另外,`Date().getTime()` 和 `System.currentTimeMillis()` 是一样的\n\n```java\npackage date;\n \n//\nimport java.util.Date;\n \npublic class TestDate {\n \n public static void main(String[] args) {\n Date now= new Date();\n \n //当前日期的毫秒数\n System.out.println(\"Date.getTime() \\t\\t\\t返回值: \"+now.getTime());\n \n //通过System.currentTimeMillis()获取当前日期的毫秒数\n System.out.println(\"System.currentTimeMillis() \\t返回值: \"+System.currentTimeMillis());\n \n }\n}\n\n/*输出:\n\nDate.getTime() 返回值: 1611591854927\nSystem.currentTimeMillis() 返回值: 1611591854927\n\n*/\n\n```\n\n\n\n## 格式化日期并转为字符串\n\n格式化日期:\n\n需要用到 `SimpleDateFormat` 类\n\n```java\nimport java.text.SimpleDateFormat;\n```\n\n1. 指定格式\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss SSS\" );\n \n //y 代表年\n //M 代表月\n //d 代表日\n //H 代表24进制的小时\n //h 代表12进制的小时\n //m 代表分钟\n //s 代表秒\n //S 代表毫秒\n \n // 以上7种可以只选其中一种或多种,根据自己需要来,如:\n SimpleDateFormat sdf1 = new SimpleDateFormat(\"yyyy-MM-dd\" );\n SimpleDateFormat sdf2 = new SimpleDateFormat(\"MM/dd\" );\n ```\n\n2. 利用sdf对象的 `format`方法对日期 `Date()`进行格式化,并返回格式化后的`字符串`\n\n ```java\n Date d = new Date();\n String str = sdf.format(d); //这样日期就按格式转为了字符串\n ```\n\n\n\n## 字符串转日期\n\n三个步骤 :\n\n1. 指定字符串格式:\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\" );\n \n //SimpleDateFormat sdf1 =new SimpleDateFormat(\"yyyy/MM/dd\" );\n ```\n\n2. 创建字符串,注意格式要与 sdf 一致\n\n ```java\n String str = \"2019-1-15 16:02:27\";\n \n //String str1 = \"2019-1-15\";\n ```\n\n3. 创建日期对象,用 sdf的 `parse(str)`方法来把字符串转为日期对象\n\n ```java\n Date d = sdf.parse(str);\n ```\n\n\n\n## 日历 Calendar\n\n使用前需要导入:\n\n```java\nimport java.util.Calendar;\n```\n\n\n\n1. 获取日历对象 `Calendar.getInstance();`\n\n ```java\n Calendar cl = Calendar.getInstance();\n ```\n\n2. 通过日历对象得到日期对象: `getTime()`\n\n ```java\n Date d = cl.getTime();\n ```\n\n3. 日期设置:把 `cl` 这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 `setTime()`\n\n ```java\n Date d2 = new Date(0);\n cl.setTime(d2);\n ```\n\n## 翻日历\n\n> add方法,在原日期上**增加(add)** 年/月/日\n> set方法,**直接设置(set)** 年/月/日\n\n1. 创建日历对象\n\n ```java\n Calendar c = Calendar.getInstance();\n ```\n\n2. 获取当前日期\n\n ```java\n Date now = c.getTime();\n ```\n\n3. 把日历改成下个月的今天\n\n ```java\n c.setTime(now); //表示把日历改为当前时间\n \n c.add(Calendar.MONTH, 1); \n //在当前时间now的基础上修改日历,这里修改了月份,下个月 = 本月 + 1\n ```\n\n4. 把日历改成去年的今天\n\n ```java\n c.setTime(now);\n c.add(Calendar.YEAR, -1); //负数表示以前的,去年 = 今年 - 1\n ```\n\n5. 把日历直接**设置**成上个月的第三天\n\n ```java\n c.setTime(now);\n c.add(Calendar.MONTH, -1); //先改月份\n c.set(Calendar.DATE, 3);\t\t//再改日数,注意用set\n \n //如果是今天后的第三天,则用:\n //c.add(Calendar.DATE, 3)\n ```\n\n ","slug":"javaSE/13-日期","published":1,"updated":"2021-08-29T15:31:31.919Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qq005zakve2q775ly3","content":"

日期

在Java中使用日期,需导入 java.util.Date

\n
1
import java.util.Date;
\n

创建一个日期对象

    \n
  • Date date = new Date();
  • \n
\n
1
2
3
4
Date now = new Date(); 

//不加参数表示当前时间
//加了参数n表示从1970-01-01 8:00:00开始经历了n毫秒
\n
    \n
  • 日期对象转字符串对象:
  • \n
\n
1
String s = now.toString()
\n

getTime()方法

getTime()是Date()对象的一个方法,返回类型 long,用于获取从1970-01-01 8:00:00开始到现在经历的毫秒

\n

另外,Date().getTime()System.currentTimeMillis() 是一样的

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package date;

//
import java.util.Date;

public class TestDate {

public static void main(String[] args) {
Date now= new Date();

//当前日期的毫秒数
System.out.println("Date.getTime() \\t\\t\\t返回值: "+now.getTime());

//通过System.currentTimeMillis()获取当前日期的毫秒数
System.out.println("System.currentTimeMillis() \\t返回值: "+System.currentTimeMillis());

}
}

/*输出:

Date.getTime() 返回值: 1611591854927
System.currentTimeMillis() 返回值: 1611591854927

*/

\n

格式化日期并转为字符串

格式化日期:

\n

需要用到 SimpleDateFormat

\n
1
import java.text.SimpleDateFormat;
\n
    \n
  1. 指定格式

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );

    //y 代表年
    //M 代表月
    //d 代表日
    //H 代表24进制的小时
    //h 代表12进制的小时
    //m 代表分钟
    //s 代表秒
    //S 代表毫秒

    // 以上7种可以只选其中一种或多种,根据自己需要来,如:
    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd" );
    SimpleDateFormat sdf2 = new SimpleDateFormat("MM/dd" );
    \n
  2. \n
  3. 利用sdf对象的 format方法对日期 Date()进行格式化,并返回格式化后的字符串

    \n
    1
    2
    Date d = new Date();
    String str = sdf.format(d); //这样日期就按格式转为了字符串
    \n
  4. \n
\n

字符串转日期

三个步骤 :

\n
    \n
  1. 指定字符串格式:

    \n
    1
    2
    3
    SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" );

    //SimpleDateFormat sdf1 =new SimpleDateFormat("yyyy/MM/dd" );
    \n
  2. \n
  3. 创建字符串,注意格式要与 sdf 一致

    \n
    1
    2
    3
    String str = "2019-1-15 16:02:27";

    //String str1 = "2019-1-15";
    \n
  4. \n
  5. 创建日期对象,用 sdf的 parse(str)方法来把字符串转为日期对象

    \n
    1
    Date d = sdf.parse(str);
    \n
  6. \n
\n

日历 Calendar

使用前需要导入:

\n
1
import java.util.Calendar;
\n
    \n
  1. 获取日历对象 Calendar.getInstance();

    \n
    1
    Calendar cl = Calendar.getInstance();
    \n
  2. \n
  3. 通过日历对象得到日期对象: getTime()

    \n
    1
    Date d = cl.getTime();
    \n
  4. \n
  5. 日期设置:把 cl 这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 setTime()

    \n
    1
    2
    Date d2 = new Date(0);
    cl.setTime(d2);
    \n
  6. \n
\n

翻日历

\n

add方法,在原日期上增加(add) 年/月/日
set方法,直接设置(set) 年/月/日

\n
\n
    \n
  1. 创建日历对象

    \n
    1
    Calendar c = Calendar.getInstance();
    \n
  2. \n
  3. 获取当前日期

    \n
    1
    Date now = c.getTime();
    \n
  4. \n
  5. 把日历改成下个月的今天

    \n
    1
    2
    3
    4
    c.setTime(now);           //表示把日历改为当前时间

    c.add(Calendar.MONTH, 1);
    //在当前时间now的基础上修改日历,这里修改了月份,下个月 = 本月 + 1
    \n
  6. \n
  7. 把日历改成去年的今天

    \n
    1
    2
    c.setTime(now);
    c.add(Calendar.YEAR, -1); //负数表示以前的,去年 = 今年 - 1
    \n
  8. \n
  9. 把日历直接设置成上个月的第三天

    \n
    1
    2
    3
    4
    5
    6
    c.setTime(now);
    c.add(Calendar.MONTH, -1); //先改月份
    c.set(Calendar.DATE, 3);\t\t//再改日数,注意用set

    //如果是今天后的第三天,则用:
    //c.add(Calendar.DATE, 3)
    \n
  10. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

日期

在Java中使用日期,需导入 java.util.Date

\n
1
import java.util.Date;
\n

创建一个日期对象

    \n
  • Date date = new Date();
  • \n
\n
1
2
3
4
Date now = new Date(); 

//不加参数表示当前时间
//加了参数n表示从1970-01-01 8:00:00开始经历了n毫秒
\n
    \n
  • 日期对象转字符串对象:
  • \n
\n
1
String s = now.toString()
\n

getTime()方法

getTime()是Date()对象的一个方法,返回类型 long,用于获取从1970-01-01 8:00:00开始到现在经历的毫秒

\n

另外,Date().getTime()System.currentTimeMillis() 是一样的

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package date;

//
import java.util.Date;

public class TestDate {

public static void main(String[] args) {
Date now= new Date();

//当前日期的毫秒数
System.out.println("Date.getTime() \\t\\t\\t返回值: "+now.getTime());

//通过System.currentTimeMillis()获取当前日期的毫秒数
System.out.println("System.currentTimeMillis() \\t返回值: "+System.currentTimeMillis());

}
}

/*输出:

Date.getTime() 返回值: 1611591854927
System.currentTimeMillis() 返回值: 1611591854927

*/

\n

格式化日期并转为字符串

格式化日期:

\n

需要用到 SimpleDateFormat

\n
1
import java.text.SimpleDateFormat;
\n
    \n
  1. 指定格式

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );

    //y 代表年
    //M 代表月
    //d 代表日
    //H 代表24进制的小时
    //h 代表12进制的小时
    //m 代表分钟
    //s 代表秒
    //S 代表毫秒

    // 以上7种可以只选其中一种或多种,根据自己需要来,如:
    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd" );
    SimpleDateFormat sdf2 = new SimpleDateFormat("MM/dd" );
    \n
  2. \n
  3. 利用sdf对象的 format方法对日期 Date()进行格式化,并返回格式化后的字符串

    \n
    1
    2
    Date d = new Date();
    String str = sdf.format(d); //这样日期就按格式转为了字符串
    \n
  4. \n
\n

字符串转日期

三个步骤 :

\n
    \n
  1. 指定字符串格式:

    \n
    1
    2
    3
    SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" );

    //SimpleDateFormat sdf1 =new SimpleDateFormat("yyyy/MM/dd" );
    \n
  2. \n
  3. 创建字符串,注意格式要与 sdf 一致

    \n
    1
    2
    3
    String str = "2019-1-15 16:02:27";

    //String str1 = "2019-1-15";
    \n
  4. \n
  5. 创建日期对象,用 sdf的 parse(str)方法来把字符串转为日期对象

    \n
    1
    Date d = sdf.parse(str);
    \n
  6. \n
\n

日历 Calendar

使用前需要导入:

\n
1
import java.util.Calendar;
\n
    \n
  1. 获取日历对象 Calendar.getInstance();

    \n
    1
    Calendar cl = Calendar.getInstance();
    \n
  2. \n
  3. 通过日历对象得到日期对象: getTime()

    \n
    1
    Date d = cl.getTime();
    \n
  4. \n
  5. 日期设置:把 cl 这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 setTime()

    \n
    1
    2
    Date d2 = new Date(0);
    cl.setTime(d2);
    \n
  6. \n
\n

翻日历

\n

add方法,在原日期上增加(add) 年/月/日
set方法,直接设置(set) 年/月/日

\n
\n
    \n
  1. 创建日历对象

    \n
    1
    Calendar c = Calendar.getInstance();
    \n
  2. \n
  3. 获取当前日期

    \n
    1
    Date now = c.getTime();
    \n
  4. \n
  5. 把日历改成下个月的今天

    \n
    1
    2
    3
    4
    c.setTime(now);           //表示把日历改为当前时间

    c.add(Calendar.MONTH, 1);
    //在当前时间now的基础上修改日历,这里修改了月份,下个月 = 本月 + 1
    \n
  6. \n
  7. 把日历改成去年的今天

    \n
    1
    2
    c.setTime(now);
    c.add(Calendar.YEAR, -1); //负数表示以前的,去年 = 今年 - 1
    \n
  8. \n
  9. 把日历直接设置成上个月的第三天

    \n
    1
    2
    3
    4
    5
    6
    c.setTime(now);
    c.add(Calendar.MONTH, -1); //先改月份
    c.set(Calendar.DATE, 3);\t\t//再改日数,注意用set

    //如果是今天后的第三天,则用:
    //c.add(Calendar.DATE, 3)
    \n
  10. \n
\n"},{"title":"17-容器","abbrlink":"c0626013","date":"2021-02-17T04:31:15.000Z","description":"java的集合(容器)介绍,关于这一内容写了2篇笔记,看完不是很理解可以看另一篇","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n# 容 器\n\n## Collectiion接口\n\nCollection接口声明了以下方法:\n\n```java\nboolean add(Object element);\nboolean remove(Object element);\nboolean contains(Object element);\n\nint size(); //容器中元素数量 \n\nboolean isEmpty();\nvoid clear();\n\nIterator iterator();\nboolean containsAll(Collection c);\nboolean addAll(Collection c);\n\nboolean removeAll(Collection c);\nboolean retainAll(Collection c); //移除非交集元素,保留交集元素\n\nObject[] toArray(); //转换成Object数组\n```\n\n\n\n> 注:List和Set是Collection的子接口\n\n\n\n### List接口\n\nList特点:有序、元素可重复\n\n- 有序:每个元素都有索引标记\n- List通常允许满足 `e1.equals(e2)` 的元素重复加入容器\n\n相比Collection接口,List中多了一些与索引有关的方法\n\n```java\nvoid add(int index, Object element);\nObject set(int index, Object element);\nObject get(int index);\nObject remove(int index);\n\nint indexOf(Object o); //返回第一个匹配元素的索引,若没有返回-1\nint lastIndexOf(Object o); //返回最后一个匹配的元素的索引,若没有返回-1\n\n```\n\n\n\n\n\n### ArrayList类\n\nArrayList底层是用【数组】实现的存储。==特点:查询效率高,增删效率低,线程不安全==。\n\n我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?\n\n本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 \n\nArrayList的Object数组【初始化长度为10】,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中\n\n\n\n### LinkList类\n\nLinkList底层用【双向链表】实现\n\n==特点:查询效率低、增删效率高,线程不安全==\n\n> 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 \n>\n> 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。\n\n\n\n### Vector类\n\nVector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。\n\n> 如何选用ArrayList、LinkedList、Vector\n\n1. 需要线程安全时,用Vector。\n\n2. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。\n\n3. 不存在线程安全问题时,增加或删除元素较多用LinkedList。\n\n\n\n## Map接口\n\n Map就是用来存储“**键(key)-值(value) 对**”的。 Map类中存储的“键值对”通过键来标识,所以“键”不能重复。\n\n\n\nMap接口常用方法:\n\n```java\nObject put(Object key, Object value);\nObject get(Object key);\nObject remove(Object key);\n\nboolean containsKey(Object Key);\nboolean containsValue(Object value);\n\nint size();\nboolean isEmpty();\n\nvoid putAll(Map t);\nvoid clear();\n```\n\n Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。\n\n\n\n### HashMap类\n\nHashMap采用**哈希算法**实现,是Map接口最常用的实现类。\n\n由于底层采用了哈希表存储数据,我们要求**键不能重复**,==如果发生重复,新的键值对会替换旧的键值对==\n\nHashMap在查找、删除、修改方面都有非常高的效率\n\n\n\n> HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。\n>\n> **HashMap与HashTable的区别**\n>\n> 1. HashMap: 线程不安全,效率高。**允许key或value为null**\n>\n> 2. HashTable: 线程安全,效率低。**不允许key或value为null**\n\n\n\n## Set接口与HashSet类\n\nSet接口继承自Collection,Set接口中**没有新增方法**,方法和Collection保持完全一致。\n\nSet容器特点:无序、不可重复\n\n> 新元素如果和Set中某个元素通过`equals()`方法对比为true,则不能加入;\n>\n> 甚至,Set中也只能放入一个null元素,不能多个。\n\nSet常用的**实现类**有:HashSet、TreeSet等,我们一般使用HashSet\n\n\n\nHashSet基本用法\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Set s = new HashSet();\n s.add(\"hello\");\n s.add(\"world\");\n System.out.println(s);\n s.add(\"hello\"); //相同的元素不会被加入\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n }\n}\n```\n\n\n\n\n\n## 迭代器\n\n\n\n将容器转为迭代器:\n\n```java\nIterator iter = aList.iterator()\n```\n\n使用Iterator迭代器遍历容器元素(List/Set/Map)\n\n- 迭代器遍历ArrayList\n\n```java\nList aList = new ArrayList();\n\nfor (int i = 0; i < 5; i++) {\n aList.add(\"a\" + i);\n}\n\nfor (Iterator iter = aList.iterator(); iter.hasNext();){\n String temp = iter.next();\n System.out.print(temp + \"\\t\");\n if (temp.endsWith(\"3\")) {// 删除3结尾的字符串\n iter.remove();\n }\n}\n```\n\n\n\n- 迭代器遍历Map 【1】\n\n```java\npublic class Test{\n public static void main(String[] args){\n Map map = new HashMap();\n map.put(\"A\", \"GaoQi\");\n map.put(\"B\", \"LiuBa\");\n Set> ss = map.entrySet();\n for(Iterator> iterator = ss.iterator(); iterator.hasNext();){\n Entry e = iterator.next();\n System.out.println(e.getKey() + \"--\" + e.getValue());\n }\n }\n}\n```\n\n\n\n- 迭代器遍历Map 【2】\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Map map = new HashMap();\n map.put(\"A\", \"GaoQi\");\n map.put(\"B\", \"LiuBa\");\n Set ss = map.keySet();\n for (Iterator iterator = ss.iterator(); iterator.hasNext();) {\n String key = iterator.next();\n System.out.println(key + \"--\" + map.get(key));\n }\n }\n}\n```\n\n\n\n\n\n## Collections工具类\n\n类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。\n\n1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。\n2. void shuffle(List) //对List容器内的元素进行随机排列。\n3. void reverse(List) //对List容器内的元素进行逆续排列 。\n4. void fill(List, Object) //用一个特定的对象重写整个List容器。\n5. int binarySearch(List, Object) //对于顺序的List容器,采用折半查找的方法查找特定对象。\n\n","source":"_posts/javaSE/17-容器.md","raw":"---\ntitle: 17-容器\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: c0626013\ndate: 2021-02-17 12:31:15\ndescription: java的集合(容器)介绍,关于这一内容写了2篇笔记,看完不是很理解可以看另一篇\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n# 容 器\n\n## Collectiion接口\n\nCollection接口声明了以下方法:\n\n```java\nboolean add(Object element);\nboolean remove(Object element);\nboolean contains(Object element);\n\nint size(); //容器中元素数量 \n\nboolean isEmpty();\nvoid clear();\n\nIterator iterator();\nboolean containsAll(Collection c);\nboolean addAll(Collection c);\n\nboolean removeAll(Collection c);\nboolean retainAll(Collection c); //移除非交集元素,保留交集元素\n\nObject[] toArray(); //转换成Object数组\n```\n\n\n\n> 注:List和Set是Collection的子接口\n\n\n\n### List接口\n\nList特点:有序、元素可重复\n\n- 有序:每个元素都有索引标记\n- List通常允许满足 `e1.equals(e2)` 的元素重复加入容器\n\n相比Collection接口,List中多了一些与索引有关的方法\n\n```java\nvoid add(int index, Object element);\nObject set(int index, Object element);\nObject get(int index);\nObject remove(int index);\n\nint indexOf(Object o); //返回第一个匹配元素的索引,若没有返回-1\nint lastIndexOf(Object o); //返回最后一个匹配的元素的索引,若没有返回-1\n\n```\n\n\n\n\n\n### ArrayList类\n\nArrayList底层是用【数组】实现的存储。==特点:查询效率高,增删效率低,线程不安全==。\n\n我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?\n\n本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 \n\nArrayList的Object数组【初始化长度为10】,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中\n\n\n\n### LinkList类\n\nLinkList底层用【双向链表】实现\n\n==特点:查询效率低、增删效率高,线程不安全==\n\n> 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 \n>\n> 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。\n\n\n\n### Vector类\n\nVector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。\n\n> 如何选用ArrayList、LinkedList、Vector\n\n1. 需要线程安全时,用Vector。\n\n2. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。\n\n3. 不存在线程安全问题时,增加或删除元素较多用LinkedList。\n\n\n\n## Map接口\n\n Map就是用来存储“**键(key)-值(value) 对**”的。 Map类中存储的“键值对”通过键来标识,所以“键”不能重复。\n\n\n\nMap接口常用方法:\n\n```java\nObject put(Object key, Object value);\nObject get(Object key);\nObject remove(Object key);\n\nboolean containsKey(Object Key);\nboolean containsValue(Object value);\n\nint size();\nboolean isEmpty();\n\nvoid putAll(Map t);\nvoid clear();\n```\n\n Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。\n\n\n\n### HashMap类\n\nHashMap采用**哈希算法**实现,是Map接口最常用的实现类。\n\n由于底层采用了哈希表存储数据,我们要求**键不能重复**,==如果发生重复,新的键值对会替换旧的键值对==\n\nHashMap在查找、删除、修改方面都有非常高的效率\n\n\n\n> HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。\n>\n> **HashMap与HashTable的区别**\n>\n> 1. HashMap: 线程不安全,效率高。**允许key或value为null**\n>\n> 2. HashTable: 线程安全,效率低。**不允许key或value为null**\n\n\n\n## Set接口与HashSet类\n\nSet接口继承自Collection,Set接口中**没有新增方法**,方法和Collection保持完全一致。\n\nSet容器特点:无序、不可重复\n\n> 新元素如果和Set中某个元素通过`equals()`方法对比为true,则不能加入;\n>\n> 甚至,Set中也只能放入一个null元素,不能多个。\n\nSet常用的**实现类**有:HashSet、TreeSet等,我们一般使用HashSet\n\n\n\nHashSet基本用法\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Set s = new HashSet();\n s.add(\"hello\");\n s.add(\"world\");\n System.out.println(s);\n s.add(\"hello\"); //相同的元素不会被加入\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n }\n}\n```\n\n\n\n\n\n## 迭代器\n\n\n\n将容器转为迭代器:\n\n```java\nIterator iter = aList.iterator()\n```\n\n使用Iterator迭代器遍历容器元素(List/Set/Map)\n\n- 迭代器遍历ArrayList\n\n```java\nList aList = new ArrayList();\n\nfor (int i = 0; i < 5; i++) {\n aList.add(\"a\" + i);\n}\n\nfor (Iterator iter = aList.iterator(); iter.hasNext();){\n String temp = iter.next();\n System.out.print(temp + \"\\t\");\n if (temp.endsWith(\"3\")) {// 删除3结尾的字符串\n iter.remove();\n }\n}\n```\n\n\n\n- 迭代器遍历Map 【1】\n\n```java\npublic class Test{\n public static void main(String[] args){\n Map map = new HashMap();\n map.put(\"A\", \"GaoQi\");\n map.put(\"B\", \"LiuBa\");\n Set> ss = map.entrySet();\n for(Iterator> iterator = ss.iterator(); iterator.hasNext();){\n Entry e = iterator.next();\n System.out.println(e.getKey() + \"--\" + e.getValue());\n }\n }\n}\n```\n\n\n\n- 迭代器遍历Map 【2】\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Map map = new HashMap();\n map.put(\"A\", \"GaoQi\");\n map.put(\"B\", \"LiuBa\");\n Set ss = map.keySet();\n for (Iterator iterator = ss.iterator(); iterator.hasNext();) {\n String key = iterator.next();\n System.out.println(key + \"--\" + map.get(key));\n }\n }\n}\n```\n\n\n\n\n\n## Collections工具类\n\n类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。\n\n1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。\n2. void shuffle(List) //对List容器内的元素进行随机排列。\n3. void reverse(List) //对List容器内的元素进行逆续排列 。\n4. void fill(List, Object) //用一个特定的对象重写整个List容器。\n5. int binarySearch(List, Object) //对于顺序的List容器,采用折半查找的方法查找特定对象。\n\n","slug":"javaSE/17-容器","published":1,"updated":"2021-08-29T15:31:56.883Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qr0063akvedrb617kz","content":"

容 器

Collectiion接口

Collection接口声明了以下方法:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
boolean add(Object element);
boolean remove(Object element);
boolean contains(Object element);

int size(); //容器中元素数量

boolean isEmpty();
void clear();

Iterator iterator();
boolean containsAll(Collection c);
boolean addAll(Collection c);

boolean removeAll(Collection c);
boolean retainAll(Collection c); //移除非交集元素,保留交集元素

Object[] toArray(); //转换成Object数组
\n
\n

注:List和Set是Collection的子接口

\n
\n

List接口

List特点:有序、元素可重复

\n
    \n
  • 有序:每个元素都有索引标记
  • \n
  • List通常允许满足 e1.equals(e2) 的元素重复加入容器
  • \n
\n

相比Collection接口,List中多了一些与索引有关的方法

\n
1
2
3
4
5
6
7
8
void add(int index, Object element);
Object set(int index, Object element);
Object get(int index);
Object remove(int index);

int indexOf(Object o); //返回第一个匹配元素的索引,若没有返回-1
int lastIndexOf(Object o); //返回最后一个匹配的元素的索引,若没有返回-1

\n

ArrayList类

ArrayList底层是用【数组】实现的存储。==特点:查询效率高,增删效率低,线程不安全==。

\n

我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?

\n

本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。

\n

ArrayList的Object数组【初始化长度为10】,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中

\n

LinkList类

LinkList底层用【双向链表】实现

\n

==特点:查询效率低、增删效率高,线程不安全==

\n
\n

双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。

\n

所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。

\n
\n

Vector类

Vector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。

\n
\n

如何选用ArrayList、LinkedList、Vector

\n
\n
    \n
  1. 需要线程安全时,用Vector。

    \n
  2. \n
  3. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。

    \n
  4. \n
  5. 不存在线程安全问题时,增加或删除元素较多用LinkedList。

    \n
  6. \n
\n

Map接口

Map就是用来存储“键(key)-值(value) 对”的。 Map类中存储的“键值对”通过键来标识,所以“键”不能重复。

\n

Map接口常用方法:

\n
1
2
3
4
5
6
7
8
9
10
11
12
Object put(Object key, Object value);
Object get(Object key);
Object remove(Object key);

boolean containsKey(Object Key);
boolean containsValue(Object value);

int size();
boolean isEmpty();

void putAll(Map t);
void clear();
\n

Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。

\n

HashMap类

HashMap采用哈希算法实现,是Map接口最常用的实现类。

\n

由于底层采用了哈希表存储数据,我们要求键不能重复,==如果发生重复,新的键值对会替换旧的键值对==

\n

HashMap在查找、删除、修改方面都有非常高的效率

\n
\n

HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。

\n

HashMap与HashTable的区别

\n
    \n
  1. HashMap: 线程不安全,效率高。允许key或value为null

    \n
  2. \n
  3. HashTable: 线程安全,效率低。不允许key或value为null

    \n
  4. \n
\n
\n

Set接口与HashSet类

Set接口继承自Collection,Set接口中没有新增方法,方法和Collection保持完全一致。

\n

Set容器特点:无序、不可重复

\n
\n

新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入;

\n

甚至,Set中也只能放入一个null元素,不能多个。

\n
\n

Set常用的实现类有:HashSet、TreeSet等,我们一般使用HashSet

\n

HashSet基本用法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args) {
Set<String> s = new HashSet<String>();
s.add("hello");
s.add("world");
System.out.println(s);
s.add("hello"); //相同的元素不会被加入
System.out.println(s);
s.add(null);
System.out.println(s);
s.add(null);
System.out.println(s);
}
}
\n

迭代器

将容器转为迭代器:

\n
1
Iterator<E> iter = aList.iterator()
\n

使用Iterator迭代器遍历容器元素(List/Set/Map)

\n
    \n
  • 迭代器遍历ArrayList
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
List<String> aList = new ArrayList<String>();

for (int i = 0; i < 5; i++) {
aList.add("a" + i);
}

for (Iterator<String> iter = aList.iterator(); iter.hasNext();){
String temp = iter.next();
System.out.print(temp + "\\t");
if (temp.endsWith("3")) {// 删除3结尾的字符串
iter.remove();
}
}
\n
    \n
  • 迭代器遍历Map 【1】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class Test{
public static void main(String[] args){
Map<String, String> map = new HashMap<String String>();
map.put("A", "GaoQi");
map.put("B", "LiuBa");
Set<Entry<String, String>> ss = map.entrySet();
for(Iterator<Entry<String, String>> iterator = ss.iterator(); iterator.hasNext();){
Entry<String, String> e = iterator.next();
System.out.println(e.getKey() + "--" + e.getValue());
}
}
}
\n
    \n
  • 迭代器遍历Map 【2】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("A", "GaoQi");
map.put("B", "LiuBa");
Set<String> ss = map.keySet();
for (Iterator<String> iterator = ss.iterator(); iterator.hasNext();) {
String key = iterator.next();
System.out.println(key + "--" + map.get(key));
}
}
}
\n

Collections工具类

类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。

\n
    \n
  1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
  2. \n
  3. void shuffle(List) //对List容器内的元素进行随机排列。
  4. \n
  5. void reverse(List) //对List容器内的元素进行逆续排列 。
  6. \n
  7. void fill(List, Object) //用一个特定的对象重写整个List容器。
  8. \n
  9. int binarySearch(List, Object) //对于顺序的List容器,采用折半查找的方法查找特定对象。
  10. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

容 器

Collectiion接口

Collection接口声明了以下方法:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
boolean add(Object element);
boolean remove(Object element);
boolean contains(Object element);

int size(); //容器中元素数量

boolean isEmpty();
void clear();

Iterator iterator();
boolean containsAll(Collection c);
boolean addAll(Collection c);

boolean removeAll(Collection c);
boolean retainAll(Collection c); //移除非交集元素,保留交集元素

Object[] toArray(); //转换成Object数组
\n
\n

注:List和Set是Collection的子接口

\n
\n

List接口

List特点:有序、元素可重复

\n
    \n
  • 有序:每个元素都有索引标记
  • \n
  • List通常允许满足 e1.equals(e2) 的元素重复加入容器
  • \n
\n

相比Collection接口,List中多了一些与索引有关的方法

\n
1
2
3
4
5
6
7
8
void add(int index, Object element);
Object set(int index, Object element);
Object get(int index);
Object remove(int index);

int indexOf(Object o); //返回第一个匹配元素的索引,若没有返回-1
int lastIndexOf(Object o); //返回最后一个匹配的元素的索引,若没有返回-1

\n

ArrayList类

ArrayList底层是用【数组】实现的存储。==特点:查询效率高,增删效率低,线程不安全==。

\n

我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?

\n

本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。

\n

ArrayList的Object数组【初始化长度为10】,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中

\n

LinkList类

LinkList底层用【双向链表】实现

\n

==特点:查询效率低、增删效率高,线程不安全==

\n
\n

双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。

\n

所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。

\n
\n

Vector类

Vector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。

\n
\n

如何选用ArrayList、LinkedList、Vector

\n
\n
    \n
  1. 需要线程安全时,用Vector。

    \n
  2. \n
  3. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。

    \n
  4. \n
  5. 不存在线程安全问题时,增加或删除元素较多用LinkedList。

    \n
  6. \n
\n

Map接口

Map就是用来存储“键(key)-值(value) 对”的。 Map类中存储的“键值对”通过键来标识,所以“键”不能重复。

\n

Map接口常用方法:

\n
1
2
3
4
5
6
7
8
9
10
11
12
Object put(Object key, Object value);
Object get(Object key);
Object remove(Object key);

boolean containsKey(Object Key);
boolean containsValue(Object value);

int size();
boolean isEmpty();

void putAll(Map t);
void clear();
\n

Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。

\n

HashMap类

HashMap采用哈希算法实现,是Map接口最常用的实现类。

\n

由于底层采用了哈希表存储数据,我们要求键不能重复,==如果发生重复,新的键值对会替换旧的键值对==

\n

HashMap在查找、删除、修改方面都有非常高的效率

\n
\n

HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。

\n

HashMap与HashTable的区别

\n
    \n
  1. HashMap: 线程不安全,效率高。允许key或value为null

    \n
  2. \n
  3. HashTable: 线程安全,效率低。不允许key或value为null

    \n
  4. \n
\n
\n

Set接口与HashSet类

Set接口继承自Collection,Set接口中没有新增方法,方法和Collection保持完全一致。

\n

Set容器特点:无序、不可重复

\n
\n

新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入;

\n

甚至,Set中也只能放入一个null元素,不能多个。

\n
\n

Set常用的实现类有:HashSet、TreeSet等,我们一般使用HashSet

\n

HashSet基本用法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args) {
Set<String> s = new HashSet<String>();
s.add("hello");
s.add("world");
System.out.println(s);
s.add("hello"); //相同的元素不会被加入
System.out.println(s);
s.add(null);
System.out.println(s);
s.add(null);
System.out.println(s);
}
}
\n

迭代器

将容器转为迭代器:

\n
1
Iterator<E> iter = aList.iterator()
\n

使用Iterator迭代器遍历容器元素(List/Set/Map)

\n
    \n
  • 迭代器遍历ArrayList
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
List<String> aList = new ArrayList<String>();

for (int i = 0; i < 5; i++) {
aList.add("a" + i);
}

for (Iterator<String> iter = aList.iterator(); iter.hasNext();){
String temp = iter.next();
System.out.print(temp + "\\t");
if (temp.endsWith("3")) {// 删除3结尾的字符串
iter.remove();
}
}
\n
    \n
  • 迭代器遍历Map 【1】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class Test{
public static void main(String[] args){
Map<String, String> map = new HashMap<String String>();
map.put("A", "GaoQi");
map.put("B", "LiuBa");
Set<Entry<String, String>> ss = map.entrySet();
for(Iterator<Entry<String, String>> iterator = ss.iterator(); iterator.hasNext();){
Entry<String, String> e = iterator.next();
System.out.println(e.getKey() + "--" + e.getValue());
}
}
}
\n
    \n
  • 迭代器遍历Map 【2】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("A", "GaoQi");
map.put("B", "LiuBa");
Set<String> ss = map.keySet();
for (Iterator<String> iterator = ss.iterator(); iterator.hasNext();) {
String key = iterator.next();
System.out.println(key + "--" + map.get(key));
}
}
}
\n

Collections工具类

类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。

\n
    \n
  1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
  2. \n
  3. void shuffle(List) //对List容器内的元素进行随机排列。
  4. \n
  5. void reverse(List) //对List容器内的元素进行逆续排列 。
  6. \n
  7. void fill(List, Object) //用一个特定的对象重写整个List容器。
  8. \n
  9. int binarySearch(List, Object) //对于顺序的List容器,采用折半查找的方法查找特定对象。
  10. \n
\n"},{"title":"2.1-类和对象的属性初始化","abbrlink":"4515cdfc","date":"2021-02-06T10:25:41.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 属性初始化\n\n## 对象属性初始化\n\n- 声明的同时初始化\n- 块初始化(声明后初始化)\n- 通过构造方法初始化\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name = \"some hero\"; //声明该属性的时候初始化\n protected float hp;\n float maxHP;\n \n {\n maxHP = 200; //块初始化\n } \n \n public Hero(){\n hp = 100; //构造方法中初始化\n \n }\n \n}\n```\n\n注意:最后进行的是通过**构造方法进行的初始化**,需要在创建对象的时候才开始初始化\n\n```java\npackage ChuShiHua;\n\npublic class Hero {\n public String name = \"some hero\";\n {\n System.out.println(\"声明时初始化:\"+name);\n }\n\n public Hero() {\n name = \"one hero\";\n System.out.println(\"构造方法中初始化:\" + name);\n }\n\n {\n name = \"the hero\";\n System.out.println(\"块初始化:\" + name);\n }\n}\n```\n\n创建对象,查看输出:\n\n```java\npackage ChuShiHua;\n\npublic class Test {\n public static void main(String[] args){\n Hero hero = new Hero();\n }\n \n}\n\n/**输出:\n声明时初始化:some hero\n块初始化:the hero\n构造方法中初始化:one hero\n*/\n```\n\n\n\n因此初始化顺序为:声明时初始化->块初始化->构造方法中初始化\n\n## 类属性初始化\n\n- 声明的同时初始化\n- **静态**块初始化\n\n```java\npackage charactor;\n \npublic class Hero {\n\n public static int itemCapacity=8; //声明的时候 初始化\n \n static{ //静态初始化块 \n itemCapacity = 6;\n }\n \n public Hero(){\n \n }\n \n public static void main(String[] args) {\n System.out.println(Hero.itemCapacity);\n }\n \n}\n```\n\n","source":"_posts/javaSE/2.1-类和对象的属性初始化.md","raw":"---\ntitle: 2.1-类和对象的属性初始化\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 4515cdfc\ndate: 2021-02-06 18:25:41\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 属性初始化\n\n## 对象属性初始化\n\n- 声明的同时初始化\n- 块初始化(声明后初始化)\n- 通过构造方法初始化\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name = \"some hero\"; //声明该属性的时候初始化\n protected float hp;\n float maxHP;\n \n {\n maxHP = 200; //块初始化\n } \n \n public Hero(){\n hp = 100; //构造方法中初始化\n \n }\n \n}\n```\n\n注意:最后进行的是通过**构造方法进行的初始化**,需要在创建对象的时候才开始初始化\n\n```java\npackage ChuShiHua;\n\npublic class Hero {\n public String name = \"some hero\";\n {\n System.out.println(\"声明时初始化:\"+name);\n }\n\n public Hero() {\n name = \"one hero\";\n System.out.println(\"构造方法中初始化:\" + name);\n }\n\n {\n name = \"the hero\";\n System.out.println(\"块初始化:\" + name);\n }\n}\n```\n\n创建对象,查看输出:\n\n```java\npackage ChuShiHua;\n\npublic class Test {\n public static void main(String[] args){\n Hero hero = new Hero();\n }\n \n}\n\n/**输出:\n声明时初始化:some hero\n块初始化:the hero\n构造方法中初始化:one hero\n*/\n```\n\n\n\n因此初始化顺序为:声明时初始化->块初始化->构造方法中初始化\n\n## 类属性初始化\n\n- 声明的同时初始化\n- **静态**块初始化\n\n```java\npackage charactor;\n \npublic class Hero {\n\n public static int itemCapacity=8; //声明的时候 初始化\n \n static{ //静态初始化块 \n itemCapacity = 6;\n }\n \n public Hero(){\n \n }\n \n public static void main(String[] args) {\n System.out.println(Hero.itemCapacity);\n }\n \n}\n```\n\n","slug":"javaSE/2.1-类和对象的属性初始化","published":1,"updated":"2021-08-29T15:30:34.836Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qs0066akve3ppb6e1q","content":"

属性初始化

对象属性初始化

    \n
  • 声明的同时初始化
  • \n
  • 块初始化(声明后初始化)
  • \n
  • 通过构造方法初始化
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package charactor;

public class Hero {
public String name = "some hero"; //声明该属性的时候初始化
protected float hp;
float maxHP;

{
maxHP = 200; //块初始化
}

public Hero(){
hp = 100; //构造方法中初始化

}

}
\n

注意:最后进行的是通过构造方法进行的初始化,需要在创建对象的时候才开始初始化

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package ChuShiHua;

public class Hero {
public String name = "some hero";
{
System.out.println("声明时初始化:"+name);
}

public Hero() {
name = "one hero";
System.out.println("构造方法中初始化:" + name);
}

{
name = "the hero";
System.out.println("块初始化:" + name);
}
}
\n

创建对象,查看输出:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package ChuShiHua;

public class Test {
public static void main(String[] args){
Hero hero = new Hero();
}

}

/**输出:
声明时初始化:some hero
块初始化:the hero
构造方法中初始化:one hero
*/
\n

因此初始化顺序为:声明时初始化->块初始化->构造方法中初始化

\n

类属性初始化

    \n
  • 声明的同时初始化
  • \n
  • 静态块初始化
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package charactor;

public class Hero {

public static int itemCapacity=8; //声明的时候 初始化

static{ //静态初始化块
itemCapacity = 6;
}

public Hero(){

}

public static void main(String[] args) {
System.out.println(Hero.itemCapacity);
}

}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

属性初始化

对象属性初始化

    \n
  • 声明的同时初始化
  • \n
  • 块初始化(声明后初始化)
  • \n
  • 通过构造方法初始化
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package charactor;

public class Hero {
public String name = "some hero"; //声明该属性的时候初始化
protected float hp;
float maxHP;

{
maxHP = 200; //块初始化
}

public Hero(){
hp = 100; //构造方法中初始化

}

}
\n

注意:最后进行的是通过构造方法进行的初始化,需要在创建对象的时候才开始初始化

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package ChuShiHua;

public class Hero {
public String name = "some hero";
{
System.out.println("声明时初始化:"+name);
}

public Hero() {
name = "one hero";
System.out.println("构造方法中初始化:" + name);
}

{
name = "the hero";
System.out.println("块初始化:" + name);
}
}
\n

创建对象,查看输出:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package ChuShiHua;

public class Test {
public static void main(String[] args){
Hero hero = new Hero();
}

}

/**输出:
声明时初始化:some hero
块初始化:the hero
构造方法中初始化:one hero
*/
\n

因此初始化顺序为:声明时初始化->块初始化->构造方法中初始化

\n

类属性初始化

    \n
  • 声明的同时初始化
  • \n
  • 静态块初始化
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package charactor;

public class Hero {

public static int itemCapacity=8; //声明的时候 初始化

static{ //静态初始化块
itemCapacity = 6;
}

public Hero(){

}

public static void main(String[] args) {
System.out.println(Hero.itemCapacity);
}

}
\n"},{"title":"17-集合框架","abbrlink":"9dc8e4af","date":"2021-02-17T03:31:29.000Z","description":"java的集合(容器)介绍,关于这一内容写了2篇笔记,看完不是很理解可以看另一篇《容器》","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 集合框架\n\n## ArrayList类\n\n### 容器概念\n\n我们已经知道,如果要存放多个对象,可以使用数组,但是数组有局限性,比如:\n\n声明长度是10的数组,不用的数组就浪费了,超过10的个数,又放不下\n\n为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是`ArrayList`,[容器的容量](https://how2j.cn/k/number-string/number-string-stringbuilder/328.html#step724)\"capacity\"会随着对象的增加,自动增长,只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。\n\n使用`ArrayList`前需导入:\n\n```java\nimport java.util.ArrayList;\n```\n\n```java\n//容器类ArrayList,用于存放对象\nArrayList heros = new ArrayList();\nheros.add( new Hero(\"盖伦\"));\nSystem.out.println(heros.size());\n\n//容器的容量\"capacity\"会随着对象的增加,自动增长\n//只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。\nheros.add( new Hero(\"提莫\"));\n```\n\n\n\n### ArrayList常用方法\n\n| 关键字 | 简介 | 示例代码 |\n| :------- | :--------------------------- | :-------------------: |\n| add | 增加 | [示例代码](#add) |\n| contains | 判断是否存在 | [示例代码](#contains) |\n| get | 获取指定位置的对象 | [示例代码](#get) |\n| indexOf | 获取对象所处的位置 | [示例代码](#indexOf) |\n| remove | 删除 | [示例代码](#remove) |\n| set | 替换 | [示例代码](#set) |\n| size | 获取大小 | [示例代码](#size) |\n| toArray | 转换为数组 | [示例代码](#toArray) |\n| addAll | 把另一个容器所有对象都加进来 | [示例代码](#addAll) |\n| clear | 清空 | [示例代码](#clear) |\n\n- ##### `add`\n\n ```java\n for (int i = 0; i < 5; i++) {\n \theros.add(new Hero(\"hero \" + i));\n }\n Hero specialHero = new Hero(\"special hero\");\n heros.add(specialHero);\n ```\n\n- ##### `contains`\n\n ```java\n // 初始化5个对象\n System.out.println(heros); //打印heros\n System.out.println(heros.contains(new Hero(\"hero 1\")));\t //false\n System.out.println(heros.contains(specialHero));\t\t//true\n ```\n\n- ##### `get`\n\n 通过get获取指定位置的对象,如果输入的下标越界,一样会报错\n\n ```java\n System.out.println(heros.get(5)); //获取指定位置的对象\n \n System.out.println(heros.get(6)); //如果超出了范围,依然会报错\n ```\n\n- ##### `indexOf`\n\n ```java\n System.out.println(\"specialHero所处的位置:\"+heros.indexOf(specialHero));\n \n System.out.println(\"新英雄,但是名字是\\\"hero 1\\\"所处的位置:\"+heros.indexOf(new Hero(\"hero 1\")));\n ```\n\n- ##### `remove`\n\n - remove可以根据下标删除ArrayList的元素\n\n ```java\n heros.remove(2);\n ```\n\n - 也可以根据对象删除\n\n ```java\n heros.remove(specialHero);\n ```\n\n- ##### `set`\n\n **set**用于替换指定位置的元素\n\n ```java\n heros.set(5, new Hero(\"hero 5\"));\n ```\n\n- ##### `size`\n\n **size** 用于获取ArrayList的大小\n\n ```java\n System.out.println(heros.size());\n ```\n\n- ##### `toArray`\n\n **toArray**可以把一个ArrayList对象转换为数组。\n 需要注意的是,如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组\n\n ```java\n Hero hs[] = (Hero[])heros.toArray(new Hero[]{});\n //传递一个Hero数组类型的对象new Hero[]{}给toArray()\n ```\n\n- ##### `addAll`\n\n **addAll** 把另一个容器所有对象都加进来\n\n ```java\n ArrayList anotherHeros = new ArrayList();\n anotherHeros.add(new Hero(\"hero a\"));\n anotherHeros.add(new Hero(\"hero b\"));\n \n heros.addAll(anotherHeros);\n ```\n\n- ##### `clear`\n\n **clear** 清空一个ArrayList\n\n ```java\n heros.clear();\n System.out.println(\"ArrayList heros:\\t\" + heros);\n ```\n\n\n\n### List接口\n\n- ArrayList与List\n\n ArrayList实现了接口List,常见的写法会把引用声明为接口List类型\n\n > 注意:是**java.util.List**,而**不是**java.awt.List\n\n ```java\n package collection;\n \n import java.util.ArrayList;\n import java.util.List;\n \n import charactor.Hero;\n \n public class TestCollection {\n \n public static void main(String[] args) {\n //ArrayList实现了接口List\n \n //常见的写法会把引用声明为接口List类型\n //注意:是java.util.List,而不是java.awt.List\n //接口引用指向子类对象(多态)\n \n List heros = new ArrayList();\n heros.add( new Hero(\"盖伦\"));\n System.out.println(heros.size());\n \n }\n \n }\n ```\n\n \n\n- List的接口与方法\n\n 因为ArrayList实现了List接口,所以List接口的方法ArrayList都实现了。\n 在【[ArrayList常用方法](#ArrayList常用方法)】有详细的讲解,在此不作赘述\n\n\n\n### 泛型\n\n- 不指定泛型的容器,可以存放任何类型的元素\n- 指定了泛型的容器,只能存放指定类型的元素以及其子类\n\n语法:\n\n```java\nList genericheros = new ArrayList(); //只能存放Hero类型\n\n//简写\nList genericheros = new ArrayList<>();\n```\n\n\n\n### 遍历\n\n| 关键字 | 简介 | 示例代码 |\n| :-------- | :-------------- | :--------------------- |\n| for | 用for循环遍历 | [示例代码](#for) |\n| iterator | 迭代器遍历 | [示例代码](#iterator) |\n| 增强型for | 用增强型for循环 | [示例代码](#增强型for) |\n\n- ##### for\n\n ```java\n for (int i = 0; i < heros.size(); i++) {\n \tHero h = heros.get(i);\n \tSystem.out.println(h);\n }\n ```\n\n- ##### iterator\n\n Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下:\n\n ```java\n import java.util.Iterator; // 引入 Iterator 类\n ```\n\n 迭代器的两个基本操作是 next 、hasNext 和 remove。\n\n - 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。\n\n - 调用 it.hasNext() 用于检测集合中是否还有元素。\n\n - 调用 it.remove() 将迭代器返回的元素删除。\n\n 首先将集合转为迭代器\n\n ```java\n Iterator it= heros.iterator();\n ```\n\n 用while遍历\n\n ```java\n //从最开始的位置判断\"下一个\"位置是否有数据\n while(it.hasNext()){ \t\t//判断是否为null\n \n Hero h = it.next(); \t//通过next取出来,并且把指针向下移动\n System.out.println(h);\n }\n ```\n\n 用for遍历\n\n ```java\n for (Iterator iterator = heros.iterator(); iterator.hasNext();) {\n Hero hero = (Hero) iterator.next();\n System.out.println(hero);\n }\n ```\n\n- ##### 增强型for\n\n ```java\n for (Hero h : heros) {\n \tSystem.out.println(h);\n }\n ```\n\n\n\n## 其他集合\n\n### 链表-LinkList\n\n以下情况使用 ArrayList :\n\n- 频繁访问列表中的某一个元素。\n- 只需要在列表末尾进行添加和删除元素操作。\n\n以下情况使用 LinkedList :\n\n- 你需要通过循环迭代来访问列表中的某些元素。\n- 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。\n\n```java\n//创建链表\nimport java.util.LinkList;\n\nLinkedList list = new LinkedList(); // 普通创建方法\n//或者\nLinkedList list = new LinkedList(Collection c); // 使用集合创建链表\n```\n\n常用方法:\n\n插入:\n\n- addLast()\n- addFirst()\n\n查看:\n\n- getFirst()\n- getLast()\n\n删除:\n\n- removeFirst()\n- removeLast()\n\n\n\n### 队列-Queue\n\nQueue是先进先出队列 FIFO,常用方法:\n\n- `offer(e)` 在最后添加元素e\n- `poll()` 取出第一个元素\n- `peek()` 查看第一个元素\n\n\n\n### 二叉树\n\n二叉树由各种**节点**组成\n\n二叉树特点:\n\n- 每个节点都可以有**左子**节点,**右子**节点\t\n- 每一个节点都有一个**值**\n\n\"image-20210212204447345\"\n\n\n\n```java\npackage collection;\n \npublic class Node {\n public Node leftNode;\t// 左子节点\n \n public Node rightNode;\t// 右子节点\n \n public Object value;\t// 值\n}\n```\n\n### HashMap\n\n1. HashMap 是一个散列表,它存储的内容是键值对`key-value`映射;\n\n2. HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步;\n\n3. HashMap 是**无序**的,即不会记录插入的顺序;\n4. HashMap中的key不能重复,value可以重复;\n5. HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。\n\n\n\"image-20210212205720886\"\n\n创建一个 HashMap 对象 Sites,其中:\n\n- key为整型\n\n- value为字符串(String)类型\n\n ```java\n import java.util.HashMap; // 引入 HashMap 类\n \n HashMap Sites = new HashMap();\n ```\n\n> 添加元素 `put(key, value)`\n>\n> ```java\n> import java.util.HashMap;\n> \n> public class HashMapTest {\n> public static void main(String[] args) {\n> // 创建 HashMap 对象 Sites\n> HashMap Sites = new HashMap();\n> // 添加键值对\n> Sites.put(1, \"Google\");\n> Sites.put(2, \"Runoob\");\n> Sites.put(3, \"Taobao\");\n> Sites.put(4, \"Zhihu\");\n> System.out.println(Sites);\n> }\n> }\n> ```\n\n> 访问元素的value值:`get(key)`\n>\n> ```java\n> System.out.println(Sites.get(3));\n> ```\n\n> 删除元素(键值对):`remove(key)`\n>\n> ```java\n> Sites.remove(4);\n> ```\n> 清空所有键值对:`clear()`\n>\n> ```java\n> Sites.clear();\n> ```\n\n---\n\n> 返回元素(键值对)数量:`size()`\n>\n> ```java\n> Sites.size();\n> ```\n\n---\n\n> 返回所有key:`keySet()`\n>\n> ```java\n> Sites.keySet()\n> ```\n>\n> 返回所有values:`values()`\n>\n> ```java\n> Sites.values()\n> ```\n\n### HashSet\n\n1. HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。\n2. HashSet 允许有 null 值。\n3. HashSet 是无序的,即不会记录插入的顺序。\n4. 由于Set是无序的,所以Set不可以获取指定位置的元素。\n5. HashSet 实现了 Set 接口。\n\n\"image-20210212212148854\"\n\n创建一个Set:\n\n```java\nHashSet sites = new HashSet();\n```\n\n- 添加元素:`sites.add(e)`\n- 判断元素e是否存在:`sites.contains(e)`\n- 删除元素:`sites.remove(e)`\n- 清空元素:`sites.clear()`\n- 计算大小:`sites.size()`\n\n### Collection\n\nCollection是 Set、List、Queue、Deque的接口(Deque 继承 Queue,间接的继承了 Collection)\n\n- Queue: 先进先出队列\n- Deque: 双向链表\n\n> 注:Collection 和 Map 之间没有关系,Collection是放一个一个对象的,Map 是放键值对的\n\n### Collections\n\nCollections是一个类,**容器**的工具类,就如同Arrays是数组的工具类\n\nCollections具有以下方法:\n\n| 关键字 | 简介 |\n| :--------------- | :----------------- |\n| reverse | 反转 |\n| shuffle | 混淆 |\n| sort | 排序 |\n| swap | 交换两个数据的位置 |\n| rotate | 滚动 |\n| synchronizedList | 线程安全化 |\n\n创建一个集合numbers\n\n```java\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n```\n\n```java\nList numbers = new ArrayList<>();\n\nfor (int i = 0; i < 10; i++) {\n numbers.add(i);\n}\n```\n\n- reverse:\n\n ```java\n Collections.reverse(numbers);\n ```\n\n- shuffle:\n\n ```java\n Collections.shuffle(numbers);\n ```\n\n- sort:\n\n ```java\n Collections.sort(numbers);\n ```\n\n- swap:\n\n ```java\n Collections.swap(numbers,0, 5); //交换第0个和第5个元素\n ```\n\n- rotate:把List中的数据,**向右**滚动指定单位的长度\n\n ```java\n Collections.rotate(numbers,2);\n ```\n\n- synchronizedList:把非线程安全的List转换为线程安全的List\n\n ```java\n //把非线程安全的List转换为线程安全的List\n List synchronizedNumbers = (List) Collections.synchronizedList(numbers);\n \n ```\n\n \n\n\n\n## 关系与区别\n\n## 其他\n\n","source":"_posts/javaSE/17-集合框架.md","raw":"---\ntitle: 17-集合框架\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 9dc8e4af\ndate: 2021-02-17 11:31:29\ndescription: java的集合(容器)介绍,关于这一内容写了2篇笔记,看完不是很理解可以看另一篇《容器》\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 集合框架\n\n## ArrayList类\n\n### 容器概念\n\n我们已经知道,如果要存放多个对象,可以使用数组,但是数组有局限性,比如:\n\n声明长度是10的数组,不用的数组就浪费了,超过10的个数,又放不下\n\n为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是`ArrayList`,[容器的容量](https://how2j.cn/k/number-string/number-string-stringbuilder/328.html#step724)\"capacity\"会随着对象的增加,自动增长,只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。\n\n使用`ArrayList`前需导入:\n\n```java\nimport java.util.ArrayList;\n```\n\n```java\n//容器类ArrayList,用于存放对象\nArrayList heros = new ArrayList();\nheros.add( new Hero(\"盖伦\"));\nSystem.out.println(heros.size());\n\n//容器的容量\"capacity\"会随着对象的增加,自动增长\n//只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。\nheros.add( new Hero(\"提莫\"));\n```\n\n\n\n### ArrayList常用方法\n\n| 关键字 | 简介 | 示例代码 |\n| :------- | :--------------------------- | :-------------------: |\n| add | 增加 | [示例代码](#add) |\n| contains | 判断是否存在 | [示例代码](#contains) |\n| get | 获取指定位置的对象 | [示例代码](#get) |\n| indexOf | 获取对象所处的位置 | [示例代码](#indexOf) |\n| remove | 删除 | [示例代码](#remove) |\n| set | 替换 | [示例代码](#set) |\n| size | 获取大小 | [示例代码](#size) |\n| toArray | 转换为数组 | [示例代码](#toArray) |\n| addAll | 把另一个容器所有对象都加进来 | [示例代码](#addAll) |\n| clear | 清空 | [示例代码](#clear) |\n\n- ##### `add`\n\n ```java\n for (int i = 0; i < 5; i++) {\n \theros.add(new Hero(\"hero \" + i));\n }\n Hero specialHero = new Hero(\"special hero\");\n heros.add(specialHero);\n ```\n\n- ##### `contains`\n\n ```java\n // 初始化5个对象\n System.out.println(heros); //打印heros\n System.out.println(heros.contains(new Hero(\"hero 1\")));\t //false\n System.out.println(heros.contains(specialHero));\t\t//true\n ```\n\n- ##### `get`\n\n 通过get获取指定位置的对象,如果输入的下标越界,一样会报错\n\n ```java\n System.out.println(heros.get(5)); //获取指定位置的对象\n \n System.out.println(heros.get(6)); //如果超出了范围,依然会报错\n ```\n\n- ##### `indexOf`\n\n ```java\n System.out.println(\"specialHero所处的位置:\"+heros.indexOf(specialHero));\n \n System.out.println(\"新英雄,但是名字是\\\"hero 1\\\"所处的位置:\"+heros.indexOf(new Hero(\"hero 1\")));\n ```\n\n- ##### `remove`\n\n - remove可以根据下标删除ArrayList的元素\n\n ```java\n heros.remove(2);\n ```\n\n - 也可以根据对象删除\n\n ```java\n heros.remove(specialHero);\n ```\n\n- ##### `set`\n\n **set**用于替换指定位置的元素\n\n ```java\n heros.set(5, new Hero(\"hero 5\"));\n ```\n\n- ##### `size`\n\n **size** 用于获取ArrayList的大小\n\n ```java\n System.out.println(heros.size());\n ```\n\n- ##### `toArray`\n\n **toArray**可以把一个ArrayList对象转换为数组。\n 需要注意的是,如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组\n\n ```java\n Hero hs[] = (Hero[])heros.toArray(new Hero[]{});\n //传递一个Hero数组类型的对象new Hero[]{}给toArray()\n ```\n\n- ##### `addAll`\n\n **addAll** 把另一个容器所有对象都加进来\n\n ```java\n ArrayList anotherHeros = new ArrayList();\n anotherHeros.add(new Hero(\"hero a\"));\n anotherHeros.add(new Hero(\"hero b\"));\n \n heros.addAll(anotherHeros);\n ```\n\n- ##### `clear`\n\n **clear** 清空一个ArrayList\n\n ```java\n heros.clear();\n System.out.println(\"ArrayList heros:\\t\" + heros);\n ```\n\n\n\n### List接口\n\n- ArrayList与List\n\n ArrayList实现了接口List,常见的写法会把引用声明为接口List类型\n\n > 注意:是**java.util.List**,而**不是**java.awt.List\n\n ```java\n package collection;\n \n import java.util.ArrayList;\n import java.util.List;\n \n import charactor.Hero;\n \n public class TestCollection {\n \n public static void main(String[] args) {\n //ArrayList实现了接口List\n \n //常见的写法会把引用声明为接口List类型\n //注意:是java.util.List,而不是java.awt.List\n //接口引用指向子类对象(多态)\n \n List heros = new ArrayList();\n heros.add( new Hero(\"盖伦\"));\n System.out.println(heros.size());\n \n }\n \n }\n ```\n\n \n\n- List的接口与方法\n\n 因为ArrayList实现了List接口,所以List接口的方法ArrayList都实现了。\n 在【[ArrayList常用方法](#ArrayList常用方法)】有详细的讲解,在此不作赘述\n\n\n\n### 泛型\n\n- 不指定泛型的容器,可以存放任何类型的元素\n- 指定了泛型的容器,只能存放指定类型的元素以及其子类\n\n语法:\n\n```java\nList genericheros = new ArrayList(); //只能存放Hero类型\n\n//简写\nList genericheros = new ArrayList<>();\n```\n\n\n\n### 遍历\n\n| 关键字 | 简介 | 示例代码 |\n| :-------- | :-------------- | :--------------------- |\n| for | 用for循环遍历 | [示例代码](#for) |\n| iterator | 迭代器遍历 | [示例代码](#iterator) |\n| 增强型for | 用增强型for循环 | [示例代码](#增强型for) |\n\n- ##### for\n\n ```java\n for (int i = 0; i < heros.size(); i++) {\n \tHero h = heros.get(i);\n \tSystem.out.println(h);\n }\n ```\n\n- ##### iterator\n\n Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下:\n\n ```java\n import java.util.Iterator; // 引入 Iterator 类\n ```\n\n 迭代器的两个基本操作是 next 、hasNext 和 remove。\n\n - 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。\n\n - 调用 it.hasNext() 用于检测集合中是否还有元素。\n\n - 调用 it.remove() 将迭代器返回的元素删除。\n\n 首先将集合转为迭代器\n\n ```java\n Iterator it= heros.iterator();\n ```\n\n 用while遍历\n\n ```java\n //从最开始的位置判断\"下一个\"位置是否有数据\n while(it.hasNext()){ \t\t//判断是否为null\n \n Hero h = it.next(); \t//通过next取出来,并且把指针向下移动\n System.out.println(h);\n }\n ```\n\n 用for遍历\n\n ```java\n for (Iterator iterator = heros.iterator(); iterator.hasNext();) {\n Hero hero = (Hero) iterator.next();\n System.out.println(hero);\n }\n ```\n\n- ##### 增强型for\n\n ```java\n for (Hero h : heros) {\n \tSystem.out.println(h);\n }\n ```\n\n\n\n## 其他集合\n\n### 链表-LinkList\n\n以下情况使用 ArrayList :\n\n- 频繁访问列表中的某一个元素。\n- 只需要在列表末尾进行添加和删除元素操作。\n\n以下情况使用 LinkedList :\n\n- 你需要通过循环迭代来访问列表中的某些元素。\n- 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。\n\n```java\n//创建链表\nimport java.util.LinkList;\n\nLinkedList list = new LinkedList(); // 普通创建方法\n//或者\nLinkedList list = new LinkedList(Collection c); // 使用集合创建链表\n```\n\n常用方法:\n\n插入:\n\n- addLast()\n- addFirst()\n\n查看:\n\n- getFirst()\n- getLast()\n\n删除:\n\n- removeFirst()\n- removeLast()\n\n\n\n### 队列-Queue\n\nQueue是先进先出队列 FIFO,常用方法:\n\n- `offer(e)` 在最后添加元素e\n- `poll()` 取出第一个元素\n- `peek()` 查看第一个元素\n\n\n\n### 二叉树\n\n二叉树由各种**节点**组成\n\n二叉树特点:\n\n- 每个节点都可以有**左子**节点,**右子**节点\t\n- 每一个节点都有一个**值**\n\n\"image-20210212204447345\"\n\n\n\n```java\npackage collection;\n \npublic class Node {\n public Node leftNode;\t// 左子节点\n \n public Node rightNode;\t// 右子节点\n \n public Object value;\t// 值\n}\n```\n\n### HashMap\n\n1. HashMap 是一个散列表,它存储的内容是键值对`key-value`映射;\n\n2. HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步;\n\n3. HashMap 是**无序**的,即不会记录插入的顺序;\n4. HashMap中的key不能重复,value可以重复;\n5. HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。\n\n\n\"image-20210212205720886\"\n\n创建一个 HashMap 对象 Sites,其中:\n\n- key为整型\n\n- value为字符串(String)类型\n\n ```java\n import java.util.HashMap; // 引入 HashMap 类\n \n HashMap Sites = new HashMap();\n ```\n\n> 添加元素 `put(key, value)`\n>\n> ```java\n> import java.util.HashMap;\n> \n> public class HashMapTest {\n> public static void main(String[] args) {\n> // 创建 HashMap 对象 Sites\n> HashMap Sites = new HashMap();\n> // 添加键值对\n> Sites.put(1, \"Google\");\n> Sites.put(2, \"Runoob\");\n> Sites.put(3, \"Taobao\");\n> Sites.put(4, \"Zhihu\");\n> System.out.println(Sites);\n> }\n> }\n> ```\n\n> 访问元素的value值:`get(key)`\n>\n> ```java\n> System.out.println(Sites.get(3));\n> ```\n\n> 删除元素(键值对):`remove(key)`\n>\n> ```java\n> Sites.remove(4);\n> ```\n> 清空所有键值对:`clear()`\n>\n> ```java\n> Sites.clear();\n> ```\n\n---\n\n> 返回元素(键值对)数量:`size()`\n>\n> ```java\n> Sites.size();\n> ```\n\n---\n\n> 返回所有key:`keySet()`\n>\n> ```java\n> Sites.keySet()\n> ```\n>\n> 返回所有values:`values()`\n>\n> ```java\n> Sites.values()\n> ```\n\n### HashSet\n\n1. HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。\n2. HashSet 允许有 null 值。\n3. HashSet 是无序的,即不会记录插入的顺序。\n4. 由于Set是无序的,所以Set不可以获取指定位置的元素。\n5. HashSet 实现了 Set 接口。\n\n\"image-20210212212148854\"\n\n创建一个Set:\n\n```java\nHashSet sites = new HashSet();\n```\n\n- 添加元素:`sites.add(e)`\n- 判断元素e是否存在:`sites.contains(e)`\n- 删除元素:`sites.remove(e)`\n- 清空元素:`sites.clear()`\n- 计算大小:`sites.size()`\n\n### Collection\n\nCollection是 Set、List、Queue、Deque的接口(Deque 继承 Queue,间接的继承了 Collection)\n\n- Queue: 先进先出队列\n- Deque: 双向链表\n\n> 注:Collection 和 Map 之间没有关系,Collection是放一个一个对象的,Map 是放键值对的\n\n### Collections\n\nCollections是一个类,**容器**的工具类,就如同Arrays是数组的工具类\n\nCollections具有以下方法:\n\n| 关键字 | 简介 |\n| :--------------- | :----------------- |\n| reverse | 反转 |\n| shuffle | 混淆 |\n| sort | 排序 |\n| swap | 交换两个数据的位置 |\n| rotate | 滚动 |\n| synchronizedList | 线程安全化 |\n\n创建一个集合numbers\n\n```java\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n```\n\n```java\nList numbers = new ArrayList<>();\n\nfor (int i = 0; i < 10; i++) {\n numbers.add(i);\n}\n```\n\n- reverse:\n\n ```java\n Collections.reverse(numbers);\n ```\n\n- shuffle:\n\n ```java\n Collections.shuffle(numbers);\n ```\n\n- sort:\n\n ```java\n Collections.sort(numbers);\n ```\n\n- swap:\n\n ```java\n Collections.swap(numbers,0, 5); //交换第0个和第5个元素\n ```\n\n- rotate:把List中的数据,**向右**滚动指定单位的长度\n\n ```java\n Collections.rotate(numbers,2);\n ```\n\n- synchronizedList:把非线程安全的List转换为线程安全的List\n\n ```java\n //把非线程安全的List转换为线程安全的List\n List synchronizedNumbers = (List) Collections.synchronizedList(numbers);\n \n ```\n\n \n\n\n\n## 关系与区别\n\n## 其他\n\n","slug":"javaSE/17-集合框架","published":1,"updated":"2021-08-29T15:31:45.529Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qt006aakvealgt52ey","content":"

集合框架

ArrayList类

容器概念

我们已经知道,如果要存放多个对象,可以使用数组,但是数组有局限性,比如:

\n

声明长度是10的数组,不用的数组就浪费了,超过10的个数,又放不下

\n

为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是ArrayList容器的容量“capacity”会随着对象的增加,自动增长,只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。

\n

使用ArrayList前需导入:

\n
1
import java.util.ArrayList;
\n
1
2
3
4
5
6
7
8
//容器类ArrayList,用于存放对象
ArrayList heros = new ArrayList();
heros.add( new Hero("盖伦"));
System.out.println(heros.size());

//容器的容量"capacity"会随着对象的增加,自动增长
//只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。
heros.add( new Hero("提莫"));
\n

ArrayList常用方法

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介示例代码
add增加示例代码
contains判断是否存在示例代码
get获取指定位置的对象示例代码
indexOf获取对象所处的位置示例代码
remove删除示例代码
set替换示例代码
size获取大小示例代码
toArray转换为数组示例代码
addAll把另一个容器所有对象都加进来示例代码
clear清空示例代码
\n
\n
    \n
  • add
    1
    2
    3
    4
    5
    for (int i = 0; i < 5; i++) {
    \theros.add(new Hero("hero " + i));
    }
    Hero specialHero = new Hero("special hero");
    heros.add(specialHero);
    \n
  • \n
  • contains
    1
    2
    3
    4
    // 初始化5个对象
    System.out.println(heros); //打印heros
    System.out.println(heros.contains(new Hero("hero 1")));\t //false
    System.out.println(heros.contains(specialHero));\t\t//true
    \n
  • \n
  • get

    通过get获取指定位置的对象,如果输入的下标越界,一样会报错

    \n
    1
    2
    3
    System.out.println(heros.get(5)); //获取指定位置的对象

    System.out.println(heros.get(6)); //如果超出了范围,依然会报错
    \n
  • \n
  • indexOf
    1
    2
    3
    System.out.println("specialHero所处的位置:"+heros.indexOf(specialHero));

    System.out.println("新英雄,但是名字是\\"hero 1\\"所处的位置:"+heros.indexOf(new Hero("hero 1")));
    \n
  • \n
  • remove
      \n
    • remove可以根据下标删除ArrayList的元素

      \n
      1
      heros.remove(2);
      \n
    • \n
    • 也可以根据对象删除

      \n
      1
      heros.remove(specialHero);
      \n
    • \n
    \n
  • \n
  • set

    set用于替换指定位置的元素

    \n
    1
    heros.set(5, new Hero("hero 5"));
    \n
  • \n
  • size

    size 用于获取ArrayList的大小

    \n
    1
    System.out.println(heros.size());
    \n
  • \n
  • toArray

    toArray可以把一个ArrayList对象转换为数组。
    需要注意的是,如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组

    \n
    1
    2
    Hero hs[] = (Hero[])heros.toArray(new Hero[]{});
    //传递一个Hero数组类型的对象new Hero[]{}给toArray()
    \n
  • \n
  • addAll

    addAll 把另一个容器所有对象都加进来

    \n
    1
    2
    3
    4
    5
    ArrayList anotherHeros = new ArrayList();
    anotherHeros.add(new Hero("hero a"));
    anotherHeros.add(new Hero("hero b"));

    heros.addAll(anotherHeros);
    \n
  • \n
  • clear

    clear 清空一个ArrayList

    \n
    1
    2
    heros.clear();
    System.out.println("ArrayList heros:\\t" + heros);
    \n
  • \n
\n

List接口

    \n
  • ArrayList与List

    \n

    ArrayList实现了接口List,常见的写法会把引用声明为接口List类型

    \n
    \n

    注意:是java.util.List,而不是java.awt.List

    \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package collection;

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

    import charactor.Hero;

    public class TestCollection {

    public static void main(String[] args) {
    //ArrayList实现了接口List

    //常见的写法会把引用声明为接口List类型
    //注意:是java.util.List,而不是java.awt.List
    //接口引用指向子类对象(多态)

    List heros = new ArrayList();
    heros.add( new Hero("盖伦"));
    System.out.println(heros.size());

    }

    }
    \n
  • \n
\n
    \n
  • List的接口与方法

    \n

    因为ArrayList实现了List接口,所以List接口的方法ArrayList都实现了。
    在【ArrayList常用方法】有详细的讲解,在此不作赘述

    \n
  • \n
\n

泛型

    \n
  • 不指定泛型的容器,可以存放任何类型的元素
  • \n
  • 指定了泛型的容器,只能存放指定类型的元素以及其子类
  • \n
\n

语法:

\n
1
2
3
4
List<Hero> genericheros = new ArrayList<Hero>(); //只能存放Hero类型

//简写
List<Hero> genericheros = new ArrayList<>();
\n

遍历

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介示例代码
for用for循环遍历示例代码
iterator迭代器遍历示例代码
增强型for用增强型for循环示例代码
\n
\n
    \n
  • for
    1
    2
    3
    4
    for (int i = 0; i < heros.size(); i++) {
    \tHero h = heros.get(i);
    \tSystem.out.println(h);
    }
    \n
  • \n
  • iterator

    Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下:

    \n
    1
    import java.util.Iterator; // 引入 Iterator 类
    \n

    迭代器的两个基本操作是 next 、hasNext 和 remove。

    \n
      \n
    • 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。

      \n
    • \n
    • 调用 it.hasNext() 用于检测集合中是否还有元素。

      \n
    • \n
    • 调用 it.remove() 将迭代器返回的元素删除。

      \n
    • \n
    \n

    首先将集合转为迭代器

    \n
    1
    Iterator<Hero> it= heros.iterator();
    \n

    用while遍历

    \n
    1
    2
    3
    4
    5
    6
    //从最开始的位置判断"下一个"位置是否有数据
    while(it.hasNext()){ \t\t//判断是否为null

    Hero h = it.next(); \t//通过next取出来,并且把指针向下移动
    System.out.println(h);
    }
    \n

    用for遍历

    \n
    1
    2
    3
    4
    for (Iterator<Hero> iterator = heros.iterator(); iterator.hasNext();) {
    Hero hero = (Hero) iterator.next();
    System.out.println(hero);
    }
    \n
  • \n
  • 增强型for
    1
    2
    3
    for (Hero h : heros) {
    \tSystem.out.println(h);
    }
    \n
  • \n
\n

其他集合

链表-LinkList

以下情况使用 ArrayList :

\n
    \n
  • 频繁访问列表中的某一个元素。
  • \n
  • 只需要在列表末尾进行添加和删除元素操作。
  • \n
\n

以下情况使用 LinkedList :

\n
    \n
  • 你需要通过循环迭代来访问列表中的某些元素。
  • \n
  • 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
  • \n
\n
1
2
3
4
5
6
//创建链表
import java.util.LinkList;

LinkedList<E> list = new LinkedList<E>(); // 普通创建方法
//或者
LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表
\n

常用方法:

\n

插入:

\n
    \n
  • addLast()
  • \n
  • addFirst()
  • \n
\n

查看:

\n
    \n
  • getFirst()
  • \n
  • getLast()
  • \n
\n

删除:

\n
    \n
  • removeFirst()
  • \n
  • removeLast()
  • \n
\n

队列-Queue

Queue是先进先出队列 FIFO,常用方法:

\n
    \n
  • offer(e) 在最后添加元素e
  • \n
  • poll() 取出第一个元素
  • \n
  • peek() 查看第一个元素
  • \n
\n

二叉树

二叉树由各种节点组成

\n

二叉树特点:

\n
    \n
  • 每个节点都可以有左子节点,右子节点
  • \n
  • 每一个节点都有一个
  • \n
\n

\"image-20210212204447345\"

\n
1
2
3
4
5
6
7
8
9
package collection;

public class Node {
public Node leftNode;\t// 左子节点

public Node rightNode;\t// 右子节点

public Object value;\t// 值
}
\n

HashMap

    \n
  1. HashMap 是一个散列表,它存储的内容是键值对key-value映射;

    \n
  2. \n
  3. HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步;

    \n
  4. \n
  5. HashMap 是无序的,即不会记录插入的顺序;

    \n
  6. \n
  7. HashMap中的key不能重复,value可以重复;
  8. \n
  9. HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。
  10. \n
\n

\"image-20210212205720886\"

\n

创建一个 HashMap 对象 Sites,其中:

\n
    \n
  • key为整型

    \n
  • \n
  • value为字符串(String)类型

    \n
    1
    2
    3
    import java.util.HashMap; // 引入 HashMap 类

    HashMap<Integer, String> Sites = new HashMap<Integer, String>();
    \n
  • \n
\n
\n

添加元素 put(key, value)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.HashMap;

public class HashMapTest {
public static void main(String[] args) {
// 创建 HashMap 对象 Sites
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
// 添加键值对
Sites.put(1, "Google");
Sites.put(2, "Runoob");
Sites.put(3, "Taobao");
Sites.put(4, "Zhihu");
System.out.println(Sites);
}
}
\n

访问元素的value值:get(key)

\n
1
System.out.println(Sites.get(3));
\n

删除元素(键值对):remove(key)

\n
1
Sites.remove(4);
\n

清空所有键值对:clear()

\n
1
Sites.clear();
\n
\n
\n
\n

返回元素(键值对)数量:size()

\n
1
Sites.size();
\n
\n
\n
\n

返回所有key:keySet()

\n
1
Sites.keySet()
\n

返回所有values:values()

\n
1
Sites.values()
\n
\n

HashSet

    \n
  1. HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
  2. \n
  3. HashSet 允许有 null 值。
  4. \n
  5. HashSet 是无序的,即不会记录插入的顺序。
  6. \n
  7. 由于Set是无序的,所以Set不可以获取指定位置的元素。
  8. \n
  9. HashSet 实现了 Set 接口。
  10. \n
\n

\"image-20210212212148854\"

\n

创建一个Set:

\n
1
HashSet<String> sites = new HashSet<String>();
\n
    \n
  • 添加元素:sites.add(e)
  • \n
  • 判断元素e是否存在:sites.contains(e)
  • \n
  • 删除元素:sites.remove(e)
  • \n
  • 清空元素:sites.clear()
  • \n
  • 计算大小:sites.size()
  • \n
\n

Collection

Collection是 Set、List、Queue、Deque的接口(Deque 继承 Queue,间接的继承了 Collection)

\n
    \n
  • Queue: 先进先出队列
  • \n
  • Deque: 双向链表
  • \n
\n
\n

注:Collection 和 Map 之间没有关系,Collection是放一个一个对象的,Map 是放键值对的

\n
\n

Collections

Collections是一个类,容器的工具类,就如同Arrays是数组的工具类

\n

Collections具有以下方法:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介
reverse反转
shuffle混淆
sort排序
swap交换两个数据的位置
rotate滚动
synchronizedList线程安全化
\n
\n

创建一个集合numbers

\n
1
2
3
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
\n
1
2
3
4
5
List<Integer> numbers = new ArrayList<>();

for (int i = 0; i < 10; i++) {
numbers.add(i);
}
\n
    \n
  • reverse:

    \n
    1
    Collections.reverse(numbers);
    \n
  • \n
  • shuffle:

    \n
    1
    Collections.shuffle(numbers);
    \n
  • \n
  • sort:

    \n
    1
    Collections.sort(numbers);
    \n
  • \n
  • swap:

    \n
    1
    Collections.swap(numbers,0, 5);  //交换第0个和第5个元素
    \n
  • \n
  • rotate:把List中的数据,向右滚动指定单位的长度

    \n
    1
    Collections.rotate(numbers,2);
    \n
  • \n
  • synchronizedList:把非线程安全的List转换为线程安全的List

    \n
    1
    2
    3
    //把非线程安全的List转换为线程安全的List
    List<Integer> synchronizedNumbers = (List<Integer>) Collections.synchronizedList(numbers);

    \n
  • \n
\n

关系与区别

其他

","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

集合框架

ArrayList类

容器概念

我们已经知道,如果要存放多个对象,可以使用数组,但是数组有局限性,比如:

\n

声明长度是10的数组,不用的数组就浪费了,超过10的个数,又放不下

\n

为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是ArrayList容器的容量“capacity”会随着对象的增加,自动增长,只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。

\n

使用ArrayList前需导入:

\n
1
import java.util.ArrayList;
\n
1
2
3
4
5
6
7
8
//容器类ArrayList,用于存放对象
ArrayList heros = new ArrayList();
heros.add( new Hero("盖伦"));
System.out.println(heros.size());

//容器的容量"capacity"会随着对象的增加,自动增长
//只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。
heros.add( new Hero("提莫"));
\n

ArrayList常用方法

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介示例代码
add增加示例代码
contains判断是否存在示例代码
get获取指定位置的对象示例代码
indexOf获取对象所处的位置示例代码
remove删除示例代码
set替换示例代码
size获取大小示例代码
toArray转换为数组示例代码
addAll把另一个容器所有对象都加进来示例代码
clear清空示例代码
\n
\n
    \n
  • add
    1
    2
    3
    4
    5
    for (int i = 0; i < 5; i++) {
    \theros.add(new Hero("hero " + i));
    }
    Hero specialHero = new Hero("special hero");
    heros.add(specialHero);
    \n
  • \n
  • contains
    1
    2
    3
    4
    // 初始化5个对象
    System.out.println(heros); //打印heros
    System.out.println(heros.contains(new Hero("hero 1")));\t //false
    System.out.println(heros.contains(specialHero));\t\t//true
    \n
  • \n
  • get

    通过get获取指定位置的对象,如果输入的下标越界,一样会报错

    \n
    1
    2
    3
    System.out.println(heros.get(5)); //获取指定位置的对象

    System.out.println(heros.get(6)); //如果超出了范围,依然会报错
    \n
  • \n
  • indexOf
    1
    2
    3
    System.out.println("specialHero所处的位置:"+heros.indexOf(specialHero));

    System.out.println("新英雄,但是名字是\\"hero 1\\"所处的位置:"+heros.indexOf(new Hero("hero 1")));
    \n
  • \n
  • remove
      \n
    • remove可以根据下标删除ArrayList的元素

      \n
      1
      heros.remove(2);
      \n
    • \n
    • 也可以根据对象删除

      \n
      1
      heros.remove(specialHero);
      \n
    • \n
    \n
  • \n
  • set

    set用于替换指定位置的元素

    \n
    1
    heros.set(5, new Hero("hero 5"));
    \n
  • \n
  • size

    size 用于获取ArrayList的大小

    \n
    1
    System.out.println(heros.size());
    \n
  • \n
  • toArray

    toArray可以把一个ArrayList对象转换为数组。
    需要注意的是,如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组

    \n
    1
    2
    Hero hs[] = (Hero[])heros.toArray(new Hero[]{});
    //传递一个Hero数组类型的对象new Hero[]{}给toArray()
    \n
  • \n
  • addAll

    addAll 把另一个容器所有对象都加进来

    \n
    1
    2
    3
    4
    5
    ArrayList anotherHeros = new ArrayList();
    anotherHeros.add(new Hero("hero a"));
    anotherHeros.add(new Hero("hero b"));

    heros.addAll(anotherHeros);
    \n
  • \n
  • clear

    clear 清空一个ArrayList

    \n
    1
    2
    heros.clear();
    System.out.println("ArrayList heros:\\t" + heros);
    \n
  • \n
\n

List接口

    \n
  • ArrayList与List

    \n

    ArrayList实现了接口List,常见的写法会把引用声明为接口List类型

    \n
    \n

    注意:是java.util.List,而不是java.awt.List

    \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package collection;

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

    import charactor.Hero;

    public class TestCollection {

    public static void main(String[] args) {
    //ArrayList实现了接口List

    //常见的写法会把引用声明为接口List类型
    //注意:是java.util.List,而不是java.awt.List
    //接口引用指向子类对象(多态)

    List heros = new ArrayList();
    heros.add( new Hero("盖伦"));
    System.out.println(heros.size());

    }

    }
    \n
  • \n
\n
    \n
  • List的接口与方法

    \n

    因为ArrayList实现了List接口,所以List接口的方法ArrayList都实现了。
    在【ArrayList常用方法】有详细的讲解,在此不作赘述

    \n
  • \n
\n

泛型

    \n
  • 不指定泛型的容器,可以存放任何类型的元素
  • \n
  • 指定了泛型的容器,只能存放指定类型的元素以及其子类
  • \n
\n

语法:

\n
1
2
3
4
List<Hero> genericheros = new ArrayList<Hero>(); //只能存放Hero类型

//简写
List<Hero> genericheros = new ArrayList<>();
\n

遍历

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介示例代码
for用for循环遍历示例代码
iterator迭代器遍历示例代码
增强型for用增强型for循环示例代码
\n
\n
    \n
  • for
    1
    2
    3
    4
    for (int i = 0; i < heros.size(); i++) {
    \tHero h = heros.get(i);
    \tSystem.out.println(h);
    }
    \n
  • \n
  • iterator

    Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下:

    \n
    1
    import java.util.Iterator; // 引入 Iterator 类
    \n

    迭代器的两个基本操作是 next 、hasNext 和 remove。

    \n
      \n
    • 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。

      \n
    • \n
    • 调用 it.hasNext() 用于检测集合中是否还有元素。

      \n
    • \n
    • 调用 it.remove() 将迭代器返回的元素删除。

      \n
    • \n
    \n

    首先将集合转为迭代器

    \n
    1
    Iterator<Hero> it= heros.iterator();
    \n

    用while遍历

    \n
    1
    2
    3
    4
    5
    6
    //从最开始的位置判断"下一个"位置是否有数据
    while(it.hasNext()){ \t\t//判断是否为null

    Hero h = it.next(); \t//通过next取出来,并且把指针向下移动
    System.out.println(h);
    }
    \n

    用for遍历

    \n
    1
    2
    3
    4
    for (Iterator<Hero> iterator = heros.iterator(); iterator.hasNext();) {
    Hero hero = (Hero) iterator.next();
    System.out.println(hero);
    }
    \n
  • \n
  • 增强型for
    1
    2
    3
    for (Hero h : heros) {
    \tSystem.out.println(h);
    }
    \n
  • \n
\n

其他集合

链表-LinkList

以下情况使用 ArrayList :

\n
    \n
  • 频繁访问列表中的某一个元素。
  • \n
  • 只需要在列表末尾进行添加和删除元素操作。
  • \n
\n

以下情况使用 LinkedList :

\n
    \n
  • 你需要通过循环迭代来访问列表中的某些元素。
  • \n
  • 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
  • \n
\n
1
2
3
4
5
6
//创建链表
import java.util.LinkList;

LinkedList<E> list = new LinkedList<E>(); // 普通创建方法
//或者
LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表
\n

常用方法:

\n

插入:

\n
    \n
  • addLast()
  • \n
  • addFirst()
  • \n
\n

查看:

\n
    \n
  • getFirst()
  • \n
  • getLast()
  • \n
\n

删除:

\n
    \n
  • removeFirst()
  • \n
  • removeLast()
  • \n
\n

队列-Queue

Queue是先进先出队列 FIFO,常用方法:

\n
    \n
  • offer(e) 在最后添加元素e
  • \n
  • poll() 取出第一个元素
  • \n
  • peek() 查看第一个元素
  • \n
\n

二叉树

二叉树由各种节点组成

\n

二叉树特点:

\n
    \n
  • 每个节点都可以有左子节点,右子节点
  • \n
  • 每一个节点都有一个
  • \n
\n

\"image-20210212204447345\"

\n
1
2
3
4
5
6
7
8
9
package collection;

public class Node {
public Node leftNode;\t// 左子节点

public Node rightNode;\t// 右子节点

public Object value;\t// 值
}
\n

HashMap

    \n
  1. HashMap 是一个散列表,它存储的内容是键值对key-value映射;

    \n
  2. \n
  3. HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步;

    \n
  4. \n
  5. HashMap 是无序的,即不会记录插入的顺序;

    \n
  6. \n
  7. HashMap中的key不能重复,value可以重复;
  8. \n
  9. HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。
  10. \n
\n

\"image-20210212205720886\"

\n

创建一个 HashMap 对象 Sites,其中:

\n
    \n
  • key为整型

    \n
  • \n
  • value为字符串(String)类型

    \n
    1
    2
    3
    import java.util.HashMap; // 引入 HashMap 类

    HashMap<Integer, String> Sites = new HashMap<Integer, String>();
    \n
  • \n
\n
\n

添加元素 put(key, value)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.HashMap;

public class HashMapTest {
public static void main(String[] args) {
// 创建 HashMap 对象 Sites
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
// 添加键值对
Sites.put(1, "Google");
Sites.put(2, "Runoob");
Sites.put(3, "Taobao");
Sites.put(4, "Zhihu");
System.out.println(Sites);
}
}
\n

访问元素的value值:get(key)

\n
1
System.out.println(Sites.get(3));
\n

删除元素(键值对):remove(key)

\n
1
Sites.remove(4);
\n

清空所有键值对:clear()

\n
1
Sites.clear();
\n
\n
\n
\n

返回元素(键值对)数量:size()

\n
1
Sites.size();
\n
\n
\n
\n

返回所有key:keySet()

\n
1
Sites.keySet()
\n

返回所有values:values()

\n
1
Sites.values()
\n
\n

HashSet

    \n
  1. HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
  2. \n
  3. HashSet 允许有 null 值。
  4. \n
  5. HashSet 是无序的,即不会记录插入的顺序。
  6. \n
  7. 由于Set是无序的,所以Set不可以获取指定位置的元素。
  8. \n
  9. HashSet 实现了 Set 接口。
  10. \n
\n

\"image-20210212212148854\"

\n

创建一个Set:

\n
1
HashSet<String> sites = new HashSet<String>();
\n
    \n
  • 添加元素:sites.add(e)
  • \n
  • 判断元素e是否存在:sites.contains(e)
  • \n
  • 删除元素:sites.remove(e)
  • \n
  • 清空元素:sites.clear()
  • \n
  • 计算大小:sites.size()
  • \n
\n

Collection

Collection是 Set、List、Queue、Deque的接口(Deque 继承 Queue,间接的继承了 Collection)

\n
    \n
  • Queue: 先进先出队列
  • \n
  • Deque: 双向链表
  • \n
\n
\n

注:Collection 和 Map 之间没有关系,Collection是放一个一个对象的,Map 是放键值对的

\n
\n

Collections

Collections是一个类,容器的工具类,就如同Arrays是数组的工具类

\n

Collections具有以下方法:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介
reverse反转
shuffle混淆
sort排序
swap交换两个数据的位置
rotate滚动
synchronizedList线程安全化
\n
\n

创建一个集合numbers

\n
1
2
3
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
\n
1
2
3
4
5
List<Integer> numbers = new ArrayList<>();

for (int i = 0; i < 10; i++) {
numbers.add(i);
}
\n
    \n
  • reverse:

    \n
    1
    Collections.reverse(numbers);
    \n
  • \n
  • shuffle:

    \n
    1
    Collections.shuffle(numbers);
    \n
  • \n
  • sort:

    \n
    1
    Collections.sort(numbers);
    \n
  • \n
  • swap:

    \n
    1
    Collections.swap(numbers,0, 5);  //交换第0个和第5个元素
    \n
  • \n
  • rotate:把List中的数据,向右滚动指定单位的长度

    \n
    1
    Collections.rotate(numbers,2);
    \n
  • \n
  • synchronizedList:把非线程安全的List转换为线程安全的List

    \n
    1
    2
    3
    //把非线程安全的List转换为线程安全的List
    List<Integer> synchronizedNumbers = (List<Integer>) Collections.synchronizedList(numbers);

    \n
  • \n
\n

关系与区别

其他

"},{"title":"18-泛型","abbrlink":"e0bbd322","date":"2021-02-18T02:32:47.000Z","description":"java泛型介绍,使用、自定义泛型、通配符","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 泛型\n\n## 泛型用法\n\n泛型的用法是在容器后面添加\\\n\n```java\nArrayList heros = new ArrayList();\n\n//简写:\nArrayList heros2 = new ArrayList<>();\n```\n\nType可以是类,抽象类,接口;\n\n泛型表示这种容器,只能存放APHero,ADHero就放不进去了。\n\n## 创建支持泛型的类\n\n1. 设计一个支持泛型的栈 MyStack\n2. 设计这个类的时候,在类的声明上,加上一个,表示该类支持泛型。\n3. T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。\n\n```java\npackage generic;\n \nimport java.util.HashMap;\nimport java.util.LinkedList;\n \nimport charactor.Hero;\nimport property.Item;\n \npublic class MyStack {\n LinkedList values = new LinkedList();\n \n public void push(T t) {\n values.addLast(t);\n }\n \n public T pull() {\n return values.removeLast();\n }\n \n public T peek() {\n return values.getLast();\n }\n \n public static void main(String[] args) {\n //在声明这个Stack的时候,使用泛型就表示该Stack只能放Hero\n MyStack heroStack = new MyStack<>();\n heroStack.push(new Hero());\n //不能放Item\n heroStack.push(new Item());\n \n //在声明这个Stack的时候,使用泛型就表示该Stack只能放Item\n MyStack itemStack = new MyStack<>();\n itemStack.push(new Item());\n //不能放Hero\n itemStack.push(new Hero());\n }\n \n}\n```\n\n\n\n## 通配符\n\n### ? extends\n\n`ArrayList heroList` 表示这是一个Hero泛型或者其子类泛型(可以理解为这个heroList的元素类型是:Hero或其子类类型):\n\n- heroList 的泛型可能是Hero\n- heroList 的泛型可能是APHero\n- heroList 的泛型可能是ADHero\n\n所以 可以确定的是,从heroList取出来的对象,一定是可以转型成Hero的\n\n```java\nArrayList apHeroList = new ArrayList();\napHeroList.add(new APHero());\n\nArrayList heroList = apHeroList;\n\n//可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的\nHero h = heroList.get(0);\n```\n\n\n\n> 注:不能往里面**放东西**,因为:\n>\n> - 放APHero就不满足\\\n> - 放ADHero又不满足\\\n>\n> ```java\n> heroList.add(new ADHero()); //编译错误,因为heroList的泛型有可能是APHero\n> ```\n\n\n\n### ? super\n\n`ArrayList heroList` 表示这是一个Hero或者其父类泛型:\n\n- heroList的泛型可能是Hero\n- heroList的泛型可能是Object\n\n可以往里面插入Hero以及Hero的子类:\n\n- 放Hero没问题\n- 放APHero、ADHero也没问题\n\n> 但是**取出来有风险**,因为不确定取出来是Hero还是Object\n\n\"?\n\n\n\n```java\nArrayList heroList = new ArrayList();\n//? super Hero 表示 heroList的泛型是Hero或者其父类泛型Object\n```\n\n```java\n//所以就可以插入Hero\nheroList.add(new Hero());\n\n//也可以插入Hero的子类\nheroList.add(new APHero());\nheroList.add(new ADHero());\n```\n\n```java\n//但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Hero会失败\nHero h= heroList.get(0);\n```\n\n\n\n### 泛型通配符【?】\n\n泛型通配符`?`代表任意泛型\n既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能;\n\n- 取:只能以Object的形式取出来\n- 放:不能往里面放对象,因为不知道到底是一个什么泛型的容器\n\n```java\nArrayList apHeroList = new ArrayList();\n\n//?泛型通配符,表示任意泛型\nArrayList generalList = apHeroList;\n\n//?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型\n//所以只能以Object的形式取出来\nObject o = generalList.get(0);\n\n//?的缺陷2: 既然?代表任意泛型,那么既有可能是Hero,也有可能是Item\n//所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去\ngeneralList.add(new Item()); //编译错误 因为?代表任意泛型,很有可能不是Item\ngeneralList.add(new Hero()); //编译错误 因为?代表任意泛型,很有可能不是Hero\ngeneralList.add(new APHero()); //编译错误 因为?代表任意泛型,很有可能不是APHero\n```\n\n\n\n### 总结\n\n如果希望只取出,不插入,就使用`? extends Hero`\n\n如果希望只插入,不取出,就使用`? super Hero`\n\n如果希望,又能插入,又能取出,就**不要**用通配符`?`\n\n\n\n## 泛型转型\n\n### 对象转型\n\n根据面向对象学习的知识,==子类转父类== 是一定可以成功的\n\n\n\n### 子类泛型转父类泛型\n\n既然子类对象转父类对象是可以成功的,那么子类泛型转父类泛型能成功吗?(不能)\n例如:\n\n- hs的泛型是父类Hero\n- adhs 的泛型是子类ADHero\n\n那么 把adhs转换为hs能成功吗?(不能)\n\n> 父类转子类也不能\n\n\n\n","source":"_posts/javaSE/18-泛型.md","raw":"---\ntitle: 18-泛型\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: e0bbd322\ndate: 2021-02-18 10:32:47\ndescription: java泛型介绍,使用、自定义泛型、通配符\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 泛型\n\n## 泛型用法\n\n泛型的用法是在容器后面添加\\\n\n```java\nArrayList heros = new ArrayList();\n\n//简写:\nArrayList heros2 = new ArrayList<>();\n```\n\nType可以是类,抽象类,接口;\n\n泛型表示这种容器,只能存放APHero,ADHero就放不进去了。\n\n## 创建支持泛型的类\n\n1. 设计一个支持泛型的栈 MyStack\n2. 设计这个类的时候,在类的声明上,加上一个,表示该类支持泛型。\n3. T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。\n\n```java\npackage generic;\n \nimport java.util.HashMap;\nimport java.util.LinkedList;\n \nimport charactor.Hero;\nimport property.Item;\n \npublic class MyStack {\n LinkedList values = new LinkedList();\n \n public void push(T t) {\n values.addLast(t);\n }\n \n public T pull() {\n return values.removeLast();\n }\n \n public T peek() {\n return values.getLast();\n }\n \n public static void main(String[] args) {\n //在声明这个Stack的时候,使用泛型就表示该Stack只能放Hero\n MyStack heroStack = new MyStack<>();\n heroStack.push(new Hero());\n //不能放Item\n heroStack.push(new Item());\n \n //在声明这个Stack的时候,使用泛型就表示该Stack只能放Item\n MyStack itemStack = new MyStack<>();\n itemStack.push(new Item());\n //不能放Hero\n itemStack.push(new Hero());\n }\n \n}\n```\n\n\n\n## 通配符\n\n### ? extends\n\n`ArrayList heroList` 表示这是一个Hero泛型或者其子类泛型(可以理解为这个heroList的元素类型是:Hero或其子类类型):\n\n- heroList 的泛型可能是Hero\n- heroList 的泛型可能是APHero\n- heroList 的泛型可能是ADHero\n\n所以 可以确定的是,从heroList取出来的对象,一定是可以转型成Hero的\n\n```java\nArrayList apHeroList = new ArrayList();\napHeroList.add(new APHero());\n\nArrayList heroList = apHeroList;\n\n//可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的\nHero h = heroList.get(0);\n```\n\n\n\n> 注:不能往里面**放东西**,因为:\n>\n> - 放APHero就不满足\\\n> - 放ADHero又不满足\\\n>\n> ```java\n> heroList.add(new ADHero()); //编译错误,因为heroList的泛型有可能是APHero\n> ```\n\n\n\n### ? super\n\n`ArrayList heroList` 表示这是一个Hero或者其父类泛型:\n\n- heroList的泛型可能是Hero\n- heroList的泛型可能是Object\n\n可以往里面插入Hero以及Hero的子类:\n\n- 放Hero没问题\n- 放APHero、ADHero也没问题\n\n> 但是**取出来有风险**,因为不确定取出来是Hero还是Object\n\n\"?\n\n\n\n```java\nArrayList heroList = new ArrayList();\n//? super Hero 表示 heroList的泛型是Hero或者其父类泛型Object\n```\n\n```java\n//所以就可以插入Hero\nheroList.add(new Hero());\n\n//也可以插入Hero的子类\nheroList.add(new APHero());\nheroList.add(new ADHero());\n```\n\n```java\n//但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Hero会失败\nHero h= heroList.get(0);\n```\n\n\n\n### 泛型通配符【?】\n\n泛型通配符`?`代表任意泛型\n既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能;\n\n- 取:只能以Object的形式取出来\n- 放:不能往里面放对象,因为不知道到底是一个什么泛型的容器\n\n```java\nArrayList apHeroList = new ArrayList();\n\n//?泛型通配符,表示任意泛型\nArrayList generalList = apHeroList;\n\n//?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型\n//所以只能以Object的形式取出来\nObject o = generalList.get(0);\n\n//?的缺陷2: 既然?代表任意泛型,那么既有可能是Hero,也有可能是Item\n//所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去\ngeneralList.add(new Item()); //编译错误 因为?代表任意泛型,很有可能不是Item\ngeneralList.add(new Hero()); //编译错误 因为?代表任意泛型,很有可能不是Hero\ngeneralList.add(new APHero()); //编译错误 因为?代表任意泛型,很有可能不是APHero\n```\n\n\n\n### 总结\n\n如果希望只取出,不插入,就使用`? extends Hero`\n\n如果希望只插入,不取出,就使用`? super Hero`\n\n如果希望,又能插入,又能取出,就**不要**用通配符`?`\n\n\n\n## 泛型转型\n\n### 对象转型\n\n根据面向对象学习的知识,==子类转父类== 是一定可以成功的\n\n\n\n### 子类泛型转父类泛型\n\n既然子类对象转父类对象是可以成功的,那么子类泛型转父类泛型能成功吗?(不能)\n例如:\n\n- hs的泛型是父类Hero\n- adhs 的泛型是子类ADHero\n\n那么 把adhs转换为hs能成功吗?(不能)\n\n> 父类转子类也不能\n\n\n\n","slug":"javaSE/18-泛型","published":1,"updated":"2021-08-29T15:32:01.869Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qu006dakve8q0q3khe","content":"

泛型

泛型用法

泛型的用法是在容器后面添加\\

\n
1
2
3
4
ArrayList<APHero> heros = new ArrayList<APHero>();

//简写:
ArrayList<Hero> heros2 = new ArrayList<>();
\n

Type可以是类,抽象类,接口;

\n

泛型表示这种容器,只能存放APHero,ADHero就放不进去了。

\n

创建支持泛型的类

    \n
  1. 设计一个支持泛型的栈 MyStack
  2. \n
  3. 设计这个类的时候,在类的声明上,加上一个,表示该类支持泛型。
  4. \n
  5. T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package generic;

import java.util.HashMap;
import java.util.LinkedList;

import charactor.Hero;
import property.Item;

public class MyStack<T> {
LinkedList<T> values = new LinkedList<T>();

public void push(T t) {
values.addLast(t);
}

public T pull() {
return values.removeLast();
}

public T peek() {
return values.getLast();
}

public static void main(String[] args) {
//在声明这个Stack的时候,使用泛型<Hero>就表示该Stack只能放Hero
MyStack<Hero> heroStack = new MyStack<>();
heroStack.push(new Hero());
//不能放Item
heroStack.push(new Item());

//在声明这个Stack的时候,使用泛型<Item>就表示该Stack只能放Item
MyStack<Item> itemStack = new MyStack<>();
itemStack.push(new Item());
//不能放Hero
itemStack.push(new Hero());
}

}
\n

通配符

? extends

ArrayList heroList<? extends Hero> 表示这是一个Hero泛型或者其子类泛型(可以理解为这个heroList的元素类型是:Hero或其子类类型):

\n
    \n
  • heroList 的泛型可能是Hero
  • \n
  • heroList 的泛型可能是APHero
  • \n
  • heroList 的泛型可能是ADHero
  • \n
\n

所以 可以确定的是,从heroList取出来的对象,一定是可以转型成Hero的

\n
1
2
3
4
5
6
7
ArrayList<APHero> apHeroList = new ArrayList<APHero>();
apHeroList.add(new APHero());

ArrayList<? extends Hero> heroList = apHeroList;

//可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的
Hero h = heroList.get(0);
\n
\n

注:不能往里面放东西,因为:

\n
    \n
  • 放APHero就不满足\\
  • \n
  • 放ADHero又不满足\\
  • \n
\n
1
heroList.add(new ADHero()); //编译错误,因为heroList的泛型有可能是APHero
\n
\n

? super

ArrayList heroList<? super Hero> 表示这是一个Hero或者其父类泛型:

\n
    \n
  • heroList的泛型可能是Hero
  • \n
  • heroList的泛型可能是Object
  • \n
\n

可以往里面插入Hero以及Hero的子类:

\n
    \n
  • 放Hero没问题
  • \n
  • 放APHero、ADHero也没问题
  • \n
\n
\n

但是取出来有风险,因为不确定取出来是Hero还是Object

\n
\n

\"?

\n
1
2
ArrayList<? super Hero> heroList = new ArrayList<Object>();
//? super Hero 表示 heroList的泛型是Hero或者其父类泛型Object
\n
1
2
3
4
5
6
//所以就可以插入Hero
heroList.add(new Hero());

//也可以插入Hero的子类
heroList.add(new APHero());
heroList.add(new ADHero());
\n
1
2
//但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Hero会失败
Hero h= heroList.get(0);
\n

泛型通配符【?】

泛型通配符?代表任意泛型
既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能;

\n
    \n
  • 取:只能以Object的形式取出来
  • \n
  • 放:不能往里面放对象,因为不知道到底是一个什么泛型的容器
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ArrayList<APHero> apHeroList = new ArrayList<APHero>();

//?泛型通配符,表示任意泛型
ArrayList<?> generalList = apHeroList;

//?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型
//所以只能以Object的形式取出来
Object o = generalList.get(0);

//?的缺陷2: 既然?代表任意泛型,那么既有可能是Hero,也有可能是Item
//所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去
generalList.add(new Item()); //编译错误 因为?代表任意泛型,很有可能不是Item
generalList.add(new Hero()); //编译错误 因为?代表任意泛型,很有可能不是Hero
generalList.add(new APHero()); //编译错误 因为?代表任意泛型,很有可能不是APHero
\n

总结

如果希望只取出,不插入,就使用? extends Hero

\n

如果希望只插入,不取出,就使用? super Hero

\n

如果希望,又能插入,又能取出,就不要用通配符

\n

泛型转型

对象转型

根据面向对象学习的知识,==子类转父类== 是一定可以成功的

\n

子类泛型转父类泛型

既然子类对象转父类对象是可以成功的,那么子类泛型转父类泛型能成功吗?(不能)
例如:

\n
    \n
  • hs的泛型是父类Hero
  • \n
  • adhs 的泛型是子类ADHero
  • \n
\n

那么 把adhs转换为hs能成功吗?(不能)

\n
\n

父类转子类也不能

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

泛型

泛型用法

泛型的用法是在容器后面添加\\

\n
1
2
3
4
ArrayList<APHero> heros = new ArrayList<APHero>();

//简写:
ArrayList<Hero> heros2 = new ArrayList<>();
\n

Type可以是类,抽象类,接口;

\n

泛型表示这种容器,只能存放APHero,ADHero就放不进去了。

\n

创建支持泛型的类

    \n
  1. 设计一个支持泛型的栈 MyStack
  2. \n
  3. 设计这个类的时候,在类的声明上,加上一个,表示该类支持泛型。
  4. \n
  5. T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package generic;

import java.util.HashMap;
import java.util.LinkedList;

import charactor.Hero;
import property.Item;

public class MyStack<T> {
LinkedList<T> values = new LinkedList<T>();

public void push(T t) {
values.addLast(t);
}

public T pull() {
return values.removeLast();
}

public T peek() {
return values.getLast();
}

public static void main(String[] args) {
//在声明这个Stack的时候,使用泛型<Hero>就表示该Stack只能放Hero
MyStack<Hero> heroStack = new MyStack<>();
heroStack.push(new Hero());
//不能放Item
heroStack.push(new Item());

//在声明这个Stack的时候,使用泛型<Item>就表示该Stack只能放Item
MyStack<Item> itemStack = new MyStack<>();
itemStack.push(new Item());
//不能放Hero
itemStack.push(new Hero());
}

}
\n

通配符

? extends

ArrayList heroList<? extends Hero> 表示这是一个Hero泛型或者其子类泛型(可以理解为这个heroList的元素类型是:Hero或其子类类型):

\n
    \n
  • heroList 的泛型可能是Hero
  • \n
  • heroList 的泛型可能是APHero
  • \n
  • heroList 的泛型可能是ADHero
  • \n
\n

所以 可以确定的是,从heroList取出来的对象,一定是可以转型成Hero的

\n
1
2
3
4
5
6
7
ArrayList<APHero> apHeroList = new ArrayList<APHero>();
apHeroList.add(new APHero());

ArrayList<? extends Hero> heroList = apHeroList;

//可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的
Hero h = heroList.get(0);
\n
\n

注:不能往里面放东西,因为:

\n
    \n
  • 放APHero就不满足\\
  • \n
  • 放ADHero又不满足\\
  • \n
\n
1
heroList.add(new ADHero()); //编译错误,因为heroList的泛型有可能是APHero
\n
\n

? super

ArrayList heroList<? super Hero> 表示这是一个Hero或者其父类泛型:

\n
    \n
  • heroList的泛型可能是Hero
  • \n
  • heroList的泛型可能是Object
  • \n
\n

可以往里面插入Hero以及Hero的子类:

\n
    \n
  • 放Hero没问题
  • \n
  • 放APHero、ADHero也没问题
  • \n
\n
\n

但是取出来有风险,因为不确定取出来是Hero还是Object

\n
\n

\"?

\n
1
2
ArrayList<? super Hero> heroList = new ArrayList<Object>();
//? super Hero 表示 heroList的泛型是Hero或者其父类泛型Object
\n
1
2
3
4
5
6
//所以就可以插入Hero
heroList.add(new Hero());

//也可以插入Hero的子类
heroList.add(new APHero());
heroList.add(new ADHero());
\n
1
2
//但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Hero会失败
Hero h= heroList.get(0);
\n

泛型通配符【?】

泛型通配符?代表任意泛型
既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能;

\n
    \n
  • 取:只能以Object的形式取出来
  • \n
  • 放:不能往里面放对象,因为不知道到底是一个什么泛型的容器
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ArrayList<APHero> apHeroList = new ArrayList<APHero>();

//?泛型通配符,表示任意泛型
ArrayList<?> generalList = apHeroList;

//?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型
//所以只能以Object的形式取出来
Object o = generalList.get(0);

//?的缺陷2: 既然?代表任意泛型,那么既有可能是Hero,也有可能是Item
//所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去
generalList.add(new Item()); //编译错误 因为?代表任意泛型,很有可能不是Item
generalList.add(new Hero()); //编译错误 因为?代表任意泛型,很有可能不是Hero
generalList.add(new APHero()); //编译错误 因为?代表任意泛型,很有可能不是APHero
\n

总结

如果希望只取出,不插入,就使用? extends Hero

\n

如果希望只插入,不取出,就使用? super Hero

\n

如果希望,又能插入,又能取出,就不要用通配符

\n

泛型转型

对象转型

根据面向对象学习的知识,==子类转父类== 是一定可以成功的

\n

子类泛型转父类泛型

既然子类对象转父类对象是可以成功的,那么子类泛型转父类泛型能成功吗?(不能)
例如:

\n
    \n
  • hs的泛型是父类Hero
  • \n
  • adhs 的泛型是子类ADHero
  • \n
\n

那么 把adhs转换为hs能成功吗?(不能)

\n
\n

父类转子类也不能

\n
\n"},{"title":"19-多线程系列","abbrlink":"c5d59ae","date":"2021-02-19T02:11:21.000Z","description":"java 多线程介绍(5种状态:新生、就绪、运行、阻塞、死亡)与使用、锁的使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 多线程系列\n\n## 线程状态\n\n\n\n一个线程对象在它的生命周期内,需要经历5个状态。\n\n![图11-4 线程生命周期图.png](https://www.sxt.cn/360shop/Public/admin/UEditor/20170526/1495787690411518.png)\n\n### 新生状态(New)\n\n用new关键字建立一个线程对象后,该线程对象就处于新生状态。\n\n处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。\n\n### 就绪状态(Runnable)\n\n处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。\n\n**就绪状态并不是执行状态**,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:\n\n1. 新建线程:调用start()方法,进入就绪状态;\n\n2. 阻塞线程:阻塞解除,进入就绪状态;\n\n3. 运行线程:调用yield()方法,直接进入就绪状态;\n\n4. 运行线程:JVM将CPU资源从本线程切换到其他线程。\n\n\n\n### 运行状态(Running)\n\n在运行状态的线程**执行自己run方法中**的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。\n\n如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。\n\n\n\n### 阻塞状态(Blocked)\n\n阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。\n\n有4种原因会导致阻塞:\n\n1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。\n\n2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。\n\n3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。\n\n4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法\n\n### 死亡状态(Terminated)\n\n死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:\n\n1. 正常运行的线程完成了它run()方法内的全部工作\n\n2. 线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程\n\n (注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。\n\n当一个线程进入死亡状态以后,就不能再回到其它状态了。\n\n\n\n\n\n## 终止线程\n\n终止线程一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。\n\n通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程。\n\n\n\n```java\npackage com.myThread;\n\npublic class TestThread implements Runnable{\n String name;\n boolean live = true;// 标记变量,表示线程是否可中止;\n public TestThread(String name) {\n super();\n this.name = name;\n }\n public void run() {\n int i = 0;\n //当live的值是true时,继续线程体;false则结束循环,继而终止线程体;\n while (live) {\n System.out.println(name + (i++));\n }\n }\n public void terminate() {\n live = false;\n }\n\n public static void main(String[] args) {\n TestThread ttc = new TestThread(\"线程A:\");\n Thread t1 = new Thread(ttc);// 新生状态\n t1.start();// 就绪状态\n for (int i = 0; i < 10; i++) {\n System.out.println(\"主线程\" + i);\n }\n ttc.terminate();\n System.out.println(\"ttc stop!\");\n }\n}\n\n\n\n```\n\n该程序中通过主线程控制live的值,当主线程把live置为 `false`时,run()方法停止执行,子线程终止运行\n\n\n\n## 暂停线程\n\n暂停线程执行常用的方法有sleep()和yield()方法\n\n这两个方法的区别是:\n\n1. sleep()方法:可以让正在运行的线程进入阻塞状态,直到**休眠时间**满了,进入就绪状态。\n\n > 注意:是休眠期到了后才会进入就绪状态,参与竞争获取CPU使用权\n\n2. yield()方法:可以让正在运行的线程**直接进入**就绪状态,**让出CPU的使用权**。\n\n > 注意:由于没有标出**让出**的时间,所以下一刻就会立即进入竞争获取CPU的就绪状态\n\n\n\n## join()方法\n\n`b.join()`可以将一个线程b插入到当前线程a中,这时a线程需要等待b执行完才会继续\n\n\n\n## 线程基本信息获取\n\n\n\n```java\nisAlive();\ngetPriority();\t//获取线程优先级,默认为5\nsetPriority(); //设置线程优先级数值(int)\n\nsetName();\ngetName();\n\ncurrentThread();\t\t\t//获得当前线程\n```\n\n> 注意:**优先级**低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。\n\n\n\n\n\n## 线程同步\n\n线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。\n\n由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。\n\n由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是`synchronized`关键字,它包括两种用法:\n\n- synchronized 方法\n- synchronized 块\n\n### synchronized 方法\n\n```java\npublic synchronized void func(int a);\n```\n\n\n\nsynchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就**独占**该锁,直到**从该方法返回**时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。\n\n\n\n### synchronized块\n\nsynchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。\n\nJava 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,**提高效率**。\n\nsynchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:\n\n```java\nsynchronized(syncObject){ \n   //允许访问控制的代码 \n}\n```\n\n表示在操控 syncObject时,每次只能控制一次,如果多个线程同时执行到这一条语句,则同一时间只能有一个线程操控syncObject\n\n\n\n## 死锁问题\n\n死锁:\n\n多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。\n\n\n\n\n\n","source":"_posts/javaSE/19-多线程系列.md","raw":"---\ntitle: 19-多线程系列\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: c5d59ae\ndate: 2021-02-19 10:11:21\ndescription: java 多线程介绍(5种状态:新生、就绪、运行、阻塞、死亡)与使用、锁的使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 多线程系列\n\n## 线程状态\n\n\n\n一个线程对象在它的生命周期内,需要经历5个状态。\n\n![图11-4 线程生命周期图.png](https://www.sxt.cn/360shop/Public/admin/UEditor/20170526/1495787690411518.png)\n\n### 新生状态(New)\n\n用new关键字建立一个线程对象后,该线程对象就处于新生状态。\n\n处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。\n\n### 就绪状态(Runnable)\n\n处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。\n\n**就绪状态并不是执行状态**,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:\n\n1. 新建线程:调用start()方法,进入就绪状态;\n\n2. 阻塞线程:阻塞解除,进入就绪状态;\n\n3. 运行线程:调用yield()方法,直接进入就绪状态;\n\n4. 运行线程:JVM将CPU资源从本线程切换到其他线程。\n\n\n\n### 运行状态(Running)\n\n在运行状态的线程**执行自己run方法中**的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。\n\n如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。\n\n\n\n### 阻塞状态(Blocked)\n\n阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。\n\n有4种原因会导致阻塞:\n\n1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。\n\n2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。\n\n3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。\n\n4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法\n\n### 死亡状态(Terminated)\n\n死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:\n\n1. 正常运行的线程完成了它run()方法内的全部工作\n\n2. 线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程\n\n (注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。\n\n当一个线程进入死亡状态以后,就不能再回到其它状态了。\n\n\n\n\n\n## 终止线程\n\n终止线程一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。\n\n通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程。\n\n\n\n```java\npackage com.myThread;\n\npublic class TestThread implements Runnable{\n String name;\n boolean live = true;// 标记变量,表示线程是否可中止;\n public TestThread(String name) {\n super();\n this.name = name;\n }\n public void run() {\n int i = 0;\n //当live的值是true时,继续线程体;false则结束循环,继而终止线程体;\n while (live) {\n System.out.println(name + (i++));\n }\n }\n public void terminate() {\n live = false;\n }\n\n public static void main(String[] args) {\n TestThread ttc = new TestThread(\"线程A:\");\n Thread t1 = new Thread(ttc);// 新生状态\n t1.start();// 就绪状态\n for (int i = 0; i < 10; i++) {\n System.out.println(\"主线程\" + i);\n }\n ttc.terminate();\n System.out.println(\"ttc stop!\");\n }\n}\n\n\n\n```\n\n该程序中通过主线程控制live的值,当主线程把live置为 `false`时,run()方法停止执行,子线程终止运行\n\n\n\n## 暂停线程\n\n暂停线程执行常用的方法有sleep()和yield()方法\n\n这两个方法的区别是:\n\n1. sleep()方法:可以让正在运行的线程进入阻塞状态,直到**休眠时间**满了,进入就绪状态。\n\n > 注意:是休眠期到了后才会进入就绪状态,参与竞争获取CPU使用权\n\n2. yield()方法:可以让正在运行的线程**直接进入**就绪状态,**让出CPU的使用权**。\n\n > 注意:由于没有标出**让出**的时间,所以下一刻就会立即进入竞争获取CPU的就绪状态\n\n\n\n## join()方法\n\n`b.join()`可以将一个线程b插入到当前线程a中,这时a线程需要等待b执行完才会继续\n\n\n\n## 线程基本信息获取\n\n\n\n```java\nisAlive();\ngetPriority();\t//获取线程优先级,默认为5\nsetPriority(); //设置线程优先级数值(int)\n\nsetName();\ngetName();\n\ncurrentThread();\t\t\t//获得当前线程\n```\n\n> 注意:**优先级**低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。\n\n\n\n\n\n## 线程同步\n\n线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。\n\n由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。\n\n由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是`synchronized`关键字,它包括两种用法:\n\n- synchronized 方法\n- synchronized 块\n\n### synchronized 方法\n\n```java\npublic synchronized void func(int a);\n```\n\n\n\nsynchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就**独占**该锁,直到**从该方法返回**时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。\n\n\n\n### synchronized块\n\nsynchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。\n\nJava 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,**提高效率**。\n\nsynchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:\n\n```java\nsynchronized(syncObject){ \n   //允许访问控制的代码 \n}\n```\n\n表示在操控 syncObject时,每次只能控制一次,如果多个线程同时执行到这一条语句,则同一时间只能有一个线程操控syncObject\n\n\n\n## 死锁问题\n\n死锁:\n\n多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。\n\n\n\n\n\n","slug":"javaSE/19-多线程系列","published":1,"updated":"2021-08-29T15:32:05.689Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qv006hakve2qm4crpn","content":"

多线程系列

线程状态

一个线程对象在它的生命周期内,需要经历5个状态。

\n

\"图11-4

\n

新生状态(New)

用new关键字建立一个线程对象后,该线程对象就处于新生状态。

\n

处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。

\n

就绪状态(Runnable)

处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。

\n

就绪状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:

\n
    \n
  1. 新建线程:调用start()方法,进入就绪状态;

    \n
  2. \n
  3. 阻塞线程:阻塞解除,进入就绪状态;

    \n
  4. \n
  5. 运行线程:调用yield()方法,直接进入就绪状态;

    \n
  6. \n
  7. 运行线程:JVM将CPU资源从本线程切换到其他线程。

    \n
  8. \n
\n

运行状态(Running)

在运行状态的线程执行自己run方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。

\n

如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。

\n

阻塞状态(Blocked)

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。

\n

有4种原因会导致阻塞:

\n
    \n
  1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。

    \n
  2. \n
  3. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。

    \n
  4. \n
  5. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。

    \n
  6. \n
  7. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法

    \n
  8. \n
\n

死亡状态(Terminated)

死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:

\n
    \n
  1. 正常运行的线程完成了它run()方法内的全部工作

    \n
  2. \n
  3. 线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程

    \n

    (注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。

    \n
  4. \n
\n

当一个线程进入死亡状态以后,就不能再回到其它状态了。

\n

终止线程

终止线程一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。

\n

通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.myThread;

public class TestThread implements Runnable{
String name;
boolean live = true;// 标记变量,表示线程是否可中止;
public TestThread(String name) {
super();
this.name = name;
}
public void run() {
int i = 0;
//当live的值是true时,继续线程体;false则结束循环,继而终止线程体;
while (live) {
System.out.println(name + (i++));
}
}
public void terminate() {
live = false;
}

public static void main(String[] args) {
TestThread ttc = new TestThread("线程A:");
Thread t1 = new Thread(ttc);// 新生状态
t1.start();// 就绪状态
for (int i = 0; i < 10; i++) {
System.out.println("主线程" + i);
}
ttc.terminate();
System.out.println("ttc stop!");
}
}



\n

该程序中通过主线程控制live的值,当主线程把live置为 false时,run()方法停止执行,子线程终止运行

\n

暂停线程

暂停线程执行常用的方法有sleep()和yield()方法

\n

这两个方法的区别是:

\n
    \n
  1. sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。

    \n
    \n

    注意:是休眠期到了后才会进入就绪状态,参与竞争获取CPU使用权

    \n
    \n
  2. \n
  3. yield()方法:可以让正在运行的线程直接进入就绪状态,让出CPU的使用权

    \n
    \n

    注意:由于没有标出让出的时间,所以下一刻就会立即进入竞争获取CPU的就绪状态

    \n
    \n
  4. \n
\n

join()方法

b.join()可以将一个线程b插入到当前线程a中,这时a线程需要等待b执行完才会继续

\n

线程基本信息获取

1
2
3
4
5
6
7
8
isAlive();
getPriority();\t//获取线程优先级,默认为5
setPriority(); //设置线程优先级数值(int)

setName();
getName();

currentThread();\t\t\t//获得当前线程
\n
\n

注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。

\n
\n

线程同步

线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。

\n

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。

\n

由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:

\n
    \n
  • synchronized 方法
  • \n
  • synchronized 块
  • \n
\n

synchronized 方法

1
public synchronized void func(int a);
\n

synchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

\n

synchronized块

synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。

\n

Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率

\n

synchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:

\n
1
2
3
synchronized(syncObject){ 
   //允许访问控制的代码
}
\n

表示在操控 syncObject时,每次只能控制一次,如果多个线程同时执行到这一条语句,则同一时间只能有一个线程操控syncObject

\n

死锁问题

死锁:

\n

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

多线程系列

线程状态

一个线程对象在它的生命周期内,需要经历5个状态。

\n

\"图11-4

\n

新生状态(New)

用new关键字建立一个线程对象后,该线程对象就处于新生状态。

\n

处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。

\n

就绪状态(Runnable)

处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。

\n

就绪状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:

\n
    \n
  1. 新建线程:调用start()方法,进入就绪状态;

    \n
  2. \n
  3. 阻塞线程:阻塞解除,进入就绪状态;

    \n
  4. \n
  5. 运行线程:调用yield()方法,直接进入就绪状态;

    \n
  6. \n
  7. 运行线程:JVM将CPU资源从本线程切换到其他线程。

    \n
  8. \n
\n

运行状态(Running)

在运行状态的线程执行自己run方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。

\n

如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。

\n

阻塞状态(Blocked)

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。

\n

有4种原因会导致阻塞:

\n
    \n
  1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。

    \n
  2. \n
  3. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。

    \n
  4. \n
  5. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。

    \n
  6. \n
  7. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法

    \n
  8. \n
\n

死亡状态(Terminated)

死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:

\n
    \n
  1. 正常运行的线程完成了它run()方法内的全部工作

    \n
  2. \n
  3. 线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程

    \n

    (注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。

    \n
  4. \n
\n

当一个线程进入死亡状态以后,就不能再回到其它状态了。

\n

终止线程

终止线程一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。

\n

通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.myThread;

public class TestThread implements Runnable{
String name;
boolean live = true;// 标记变量,表示线程是否可中止;
public TestThread(String name) {
super();
this.name = name;
}
public void run() {
int i = 0;
//当live的值是true时,继续线程体;false则结束循环,继而终止线程体;
while (live) {
System.out.println(name + (i++));
}
}
public void terminate() {
live = false;
}

public static void main(String[] args) {
TestThread ttc = new TestThread("线程A:");
Thread t1 = new Thread(ttc);// 新生状态
t1.start();// 就绪状态
for (int i = 0; i < 10; i++) {
System.out.println("主线程" + i);
}
ttc.terminate();
System.out.println("ttc stop!");
}
}



\n

该程序中通过主线程控制live的值,当主线程把live置为 false时,run()方法停止执行,子线程终止运行

\n

暂停线程

暂停线程执行常用的方法有sleep()和yield()方法

\n

这两个方法的区别是:

\n
    \n
  1. sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。

    \n
    \n

    注意:是休眠期到了后才会进入就绪状态,参与竞争获取CPU使用权

    \n
    \n
  2. \n
  3. yield()方法:可以让正在运行的线程直接进入就绪状态,让出CPU的使用权

    \n
    \n

    注意:由于没有标出让出的时间,所以下一刻就会立即进入竞争获取CPU的就绪状态

    \n
    \n
  4. \n
\n

join()方法

b.join()可以将一个线程b插入到当前线程a中,这时a线程需要等待b执行完才会继续

\n

线程基本信息获取

1
2
3
4
5
6
7
8
isAlive();
getPriority();\t//获取线程优先级,默认为5
setPriority(); //设置线程优先级数值(int)

setName();
getName();

currentThread();\t\t\t//获得当前线程
\n
\n

注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。

\n
\n

线程同步

线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。

\n

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。

\n

由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:

\n
    \n
  • synchronized 方法
  • \n
  • synchronized 块
  • \n
\n

synchronized 方法

1
public synchronized void func(int a);
\n

synchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

\n

synchronized块

synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。

\n

Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率

\n

synchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:

\n
1
2
3
synchronized(syncObject){ 
   //允许访问控制的代码
}
\n

表示在操控 syncObject时,每次只能控制一次,如果多个线程同时执行到这一条语句,则同一时间只能有一个线程操控syncObject

\n

死锁问题

死锁:

\n

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。

\n"},{"title":"2.2-访问修饰符","abbrlink":"51a2ab89","date":"2021-02-07T04:12:26.000Z","description":"Java的访问修饰符有4个,默认没有写出来,是default,其他3个是 public、private、protected","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 访问修饰符\n\n## 区分:\n\n有 `public`、`private`、`protected`\n\n没有修饰符即为默认:`package/friendly/default`\n\n- private:只有类自身可以访问;由该类实例的对象不能访问,子类不能继承\n\n- public:所有类均可以继承、访问:(同包/不同包)子类可继承;(同包/不同包)类可以访问\n\n- protected:不同包且没有继承关系的类不能访问\n\n- 没有修饰符(package):只能在自己包使用;不同包不能访问、继承\n\n- 总结(红色字体表示不可行)![总结](https://stepimagewm.how2j.cn/612.png)\n\n ​\t\t\t\t\n\n> 下面以Hero为研究对象,弄清楚各个类之间的关系\n>\n> **自身:**指的是Hero自己\n> **同包子类:**ADHero这个类是Hero的子类,并且和Hero处于同一个包下\n> **不同包子类:**Support这个类是Hero的子类,但是在另一个包下\n> **同包类:** GiantDragon 这个类和Hero是**同一个包**,但是彼此没有继承关系\n> **其他类:**Item这个类,**在不同包**,也没有继承关系的类\n>\n> ![](https://stepimagewm.how2j.cn/605.png)\n\n\n\n## 什么情况使用什么修饰符\n\n> 1. 属性通常使用private封装起来\n>\n> 2. 方法一般使用public用于被调用\n>\n> 3. 会被子类继承的方法,通常使用protected\n>\n> 4. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西\n\n---\n\n> **作用范围最小原则:**\n> 简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要**露出来的**,就不用**露出来**了","source":"_posts/javaSE/2.2-访问修饰符.md","raw":"---\ntitle: 2.2-访问修饰符\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 51a2ab89\ndate: 2021-02-07 12:12:26\ndescription: Java的访问修饰符有4个,默认没有写出来,是default,其他3个是 public、private、protected\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 访问修饰符\n\n## 区分:\n\n有 `public`、`private`、`protected`\n\n没有修饰符即为默认:`package/friendly/default`\n\n- private:只有类自身可以访问;由该类实例的对象不能访问,子类不能继承\n\n- public:所有类均可以继承、访问:(同包/不同包)子类可继承;(同包/不同包)类可以访问\n\n- protected:不同包且没有继承关系的类不能访问\n\n- 没有修饰符(package):只能在自己包使用;不同包不能访问、继承\n\n- 总结(红色字体表示不可行)![总结](https://stepimagewm.how2j.cn/612.png)\n\n ​\t\t\t\t\n\n> 下面以Hero为研究对象,弄清楚各个类之间的关系\n>\n> **自身:**指的是Hero自己\n> **同包子类:**ADHero这个类是Hero的子类,并且和Hero处于同一个包下\n> **不同包子类:**Support这个类是Hero的子类,但是在另一个包下\n> **同包类:** GiantDragon 这个类和Hero是**同一个包**,但是彼此没有继承关系\n> **其他类:**Item这个类,**在不同包**,也没有继承关系的类\n>\n> ![](https://stepimagewm.how2j.cn/605.png)\n\n\n\n## 什么情况使用什么修饰符\n\n> 1. 属性通常使用private封装起来\n>\n> 2. 方法一般使用public用于被调用\n>\n> 3. 会被子类继承的方法,通常使用protected\n>\n> 4. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西\n\n---\n\n> **作用范围最小原则:**\n> 简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要**露出来的**,就不用**露出来**了","slug":"javaSE/2.2-访问修饰符","published":1,"updated":"2021-08-29T15:30:38.950Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qw006kakvec7h0d155","content":"

访问修饰符

区分:

publicprivateprotected

\n

没有修饰符即为默认:package/friendly/default

\n
    \n
  • private:只有类自身可以访问;由该类实例的对象不能访问,子类不能继承

    \n
  • \n
  • public:所有类均可以继承、访问:(同包/不同包)子类可继承;(同包/不同包)类可以访问

    \n
  • \n
  • protected:不同包且没有继承关系的类不能访问

    \n
  • \n
  • 没有修饰符(package):只能在自己包使用;不同包不能访问、继承

    \n
  • \n
  • 总结(红色字体表示不可行)\"总结\"

    \n

    \n
  • \n
\n
\n

下面以Hero为研究对象,弄清楚各个类之间的关系

\n

自身:指的是Hero自己
同包子类:ADHero这个类是Hero的子类,并且和Hero处于同一个包下
不同包子类:Support这个类是Hero的子类,但是在另一个包下
同包类: GiantDragon 这个类和Hero是同一个包,但是彼此没有继承关系
其他类:Item这个类,在不同包,也没有继承关系的类

\n

\"\"

\n
\n

什么情况使用什么修饰符

\n
    \n
  1. 属性通常使用private封装起来

    \n
  2. \n
  3. 方法一般使用public用于被调用

    \n
  4. \n
  5. 会被子类继承的方法,通常使用protected

    \n
  6. \n
  7. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西

    \n
  8. \n
\n
\n
\n
\n

作用范围最小原则:
简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

访问修饰符

区分:

publicprivateprotected

\n

没有修饰符即为默认:package/friendly/default

\n
    \n
  • private:只有类自身可以访问;由该类实例的对象不能访问,子类不能继承

    \n
  • \n
  • public:所有类均可以继承、访问:(同包/不同包)子类可继承;(同包/不同包)类可以访问

    \n
  • \n
  • protected:不同包且没有继承关系的类不能访问

    \n
  • \n
  • 没有修饰符(package):只能在自己包使用;不同包不能访问、继承

    \n
  • \n
  • 总结(红色字体表示不可行)\"总结\"

    \n

    \n
  • \n
\n
\n

下面以Hero为研究对象,弄清楚各个类之间的关系

\n

自身:指的是Hero自己
同包子类:ADHero这个类是Hero的子类,并且和Hero处于同一个包下
不同包子类:Support这个类是Hero的子类,但是在另一个包下
同包类: GiantDragon 这个类和Hero是同一个包,但是彼此没有继承关系
其他类:Item这个类,在不同包,也没有继承关系的类

\n

\"\"

\n
\n

什么情况使用什么修饰符

\n
    \n
  1. 属性通常使用private封装起来

    \n
  2. \n
  3. 方法一般使用public用于被调用

    \n
  4. \n
  5. 会被子类继承的方法,通常使用protected

    \n
  6. \n
  7. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西

    \n
  8. \n
\n
\n
\n
\n

作用范围最小原则:
简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来

\n
\n"},{"title":"20-反射机制","abbrlink":"c9f91dab","date":"2021-02-20T04:13:21.000Z","description":"java反射的使用(获取类对象、创建对象、属性访问、方法调用等)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# Java反射机制\n\n\n\n## 获取类对象\n\n### 类对象\n\n**类对象**,用于描述这种类,都有什么属性,什么方法的\n\n\n\n### 获取类对象\n\n- Class.forName();\n\n- Hero.class\n\n (注:Hero是自定义类的类名)\n\n- new Hero().getClass()\n\n在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。\n\n\n\n```java\nString className = \"charactor.Hero\";\n\nClass pClass1 = Class.forName(className);\nClass pClass2 = Hero.class;\nClass pClass3 = new Hero().getClass();\n\nSystem.out.println(pClass1 == pClass2);//true\nSystem.out.println(pClass1 == pClass3);//true\n```\n\n\n\n### 获取类对象,会导致类属性的初始化\n\n无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。\n\n(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)\n\n\n\n## 创建类对象\n\n使用反射机制创建类对象,3个步骤\n\n1. 获取类对象\n2. 获取构造器\n3. 通过构造器实例化\n\n```java\n//使用反射的方式创建对象\nString className = \"charactor.Hero\";\n//类对象\nClass pClass = Class.forName(className);\n\n//构造器\nConstructor c = pClass.getConstructor();\n\n//通过构造器实例化\nHero h2 = (Hero) c.newInstance();\n```\n\n\n\n## 属性访问\n\n### 用法\n\n使用反射机制获取并修改类对象属性(也叫字段`Field`),4个步骤\n\n1. 导包\n2. 获取类对象\n3. 获取属性(**字段**)—— `getDeclaredField`\n4. 修改指定对象的属性值—— `set`\n\n```java\nimport java.lang.reflect.Field;\n\nHero h = new Hero();\n\nClass c = h.getClass();\n\nField f = c.getDeclaredField(\"name\");\n\nf1.set(h, \"teemo\");//修改这个字段的值\n```\n\n\n\n### 区分 getField 与 getDeclaredField\n\n- 这两个方法都是用于获取字段\n\n- `getField` **只能获取**public的,包括**从父类继承**来的字段。\n\n- `getDeclaredField` 可以获取本类所有的字段,**包括private**的,但是**不能获取继承**来的字段。\n\n **注**: 这里只能获取到private的**字段**,但并不能访问该private字段的**值**,除非加上**setAccessible(true)**\n\n\n\n## 方法获取\n\n1. 导包\n2. 获取类对象\n3. 获取到方法—— `getMethod`\n4. 调用指定对象的方法—— `invoke`\n\n```java\nimport java.lang.reflect.Method;\n\nHero h = new Hero();\n \n// 获取这个名字叫做setName,参数类型是String的方法\nMethod m = h.getClass().getMethod(\"setName\", String.class);\n\n// 对h对象,调用这个方法\nm.invoke(h, \"garent\");\n```\n\n","source":"_posts/javaSE/20-反射机制.md","raw":"---\ntitle: 20-反射机制\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: c9f91dab\ndate: 2021-02-20 12:13:21\ndescription: java反射的使用(获取类对象、创建对象、属性访问、方法调用等)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# Java反射机制\n\n\n\n## 获取类对象\n\n### 类对象\n\n**类对象**,用于描述这种类,都有什么属性,什么方法的\n\n\n\n### 获取类对象\n\n- Class.forName();\n\n- Hero.class\n\n (注:Hero是自定义类的类名)\n\n- new Hero().getClass()\n\n在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。\n\n\n\n```java\nString className = \"charactor.Hero\";\n\nClass pClass1 = Class.forName(className);\nClass pClass2 = Hero.class;\nClass pClass3 = new Hero().getClass();\n\nSystem.out.println(pClass1 == pClass2);//true\nSystem.out.println(pClass1 == pClass3);//true\n```\n\n\n\n### 获取类对象,会导致类属性的初始化\n\n无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。\n\n(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)\n\n\n\n## 创建类对象\n\n使用反射机制创建类对象,3个步骤\n\n1. 获取类对象\n2. 获取构造器\n3. 通过构造器实例化\n\n```java\n//使用反射的方式创建对象\nString className = \"charactor.Hero\";\n//类对象\nClass pClass = Class.forName(className);\n\n//构造器\nConstructor c = pClass.getConstructor();\n\n//通过构造器实例化\nHero h2 = (Hero) c.newInstance();\n```\n\n\n\n## 属性访问\n\n### 用法\n\n使用反射机制获取并修改类对象属性(也叫字段`Field`),4个步骤\n\n1. 导包\n2. 获取类对象\n3. 获取属性(**字段**)—— `getDeclaredField`\n4. 修改指定对象的属性值—— `set`\n\n```java\nimport java.lang.reflect.Field;\n\nHero h = new Hero();\n\nClass c = h.getClass();\n\nField f = c.getDeclaredField(\"name\");\n\nf1.set(h, \"teemo\");//修改这个字段的值\n```\n\n\n\n### 区分 getField 与 getDeclaredField\n\n- 这两个方法都是用于获取字段\n\n- `getField` **只能获取**public的,包括**从父类继承**来的字段。\n\n- `getDeclaredField` 可以获取本类所有的字段,**包括private**的,但是**不能获取继承**来的字段。\n\n **注**: 这里只能获取到private的**字段**,但并不能访问该private字段的**值**,除非加上**setAccessible(true)**\n\n\n\n## 方法获取\n\n1. 导包\n2. 获取类对象\n3. 获取到方法—— `getMethod`\n4. 调用指定对象的方法—— `invoke`\n\n```java\nimport java.lang.reflect.Method;\n\nHero h = new Hero();\n \n// 获取这个名字叫做setName,参数类型是String的方法\nMethod m = h.getClass().getMethod(\"setName\", String.class);\n\n// 对h对象,调用这个方法\nm.invoke(h, \"garent\");\n```\n\n","slug":"javaSE/20-反射机制","published":1,"updated":"2021-08-29T15:32:10.453Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qx006oakveh6kj9q64","content":"

Java反射机制

获取类对象

类对象

类对象,用于描述这种类,都有什么属性,什么方法的

\n

获取类对象

    \n
  • Class.forName();

    \n
  • \n
  • Hero.class

    \n

    (注:Hero是自定义类的类名)

    \n
  • \n
  • new Hero().getClass()

    \n
  • \n
\n

在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。

\n
1
2
3
4
5
6
7
8
String className = "charactor.Hero";

Class pClass1 = Class.forName(className);
Class pClass2 = Hero.class;
Class pClass3 = new Hero().getClass();

System.out.println(pClass1 == pClass2);//true
System.out.println(pClass1 == pClass3);//true
\n

获取类对象,会导致类属性的初始化

无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。

\n

(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)

\n

创建类对象

使用反射机制创建类对象,3个步骤

\n
    \n
  1. 获取类对象
  2. \n
  3. 获取构造器
  4. \n
  5. 通过构造器实例化
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
//使用反射的方式创建对象
String className = "charactor.Hero";
//类对象
Class pClass = Class.forName(className);

//构造器
Constructor c = pClass.getConstructor();

//通过构造器实例化
Hero h2 = (Hero) c.newInstance();
\n

属性访问

用法

使用反射机制获取并修改类对象属性(也叫字段Field),4个步骤

\n
    \n
  1. 导包
  2. \n
  3. 获取类对象
  4. \n
  5. 获取属性(字段)—— getDeclaredField
  6. \n
  7. 修改指定对象的属性值—— set
  8. \n
\n
1
2
3
4
5
6
7
8
9
import java.lang.reflect.Field;

Hero h = new Hero();

Class c = h.getClass();

Field f = c.getDeclaredField("name");

f1.set(h, "teemo");//修改这个字段的值
\n

区分 getField 与 getDeclaredField

    \n
  • 这两个方法都是用于获取字段

    \n
  • \n
  • getField 只能获取public的,包括从父类继承来的字段。

    \n
  • \n
  • getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。

    \n

    : 这里只能获取到private的字段,但并不能访问该private字段的,除非加上setAccessible(true)

    \n
  • \n
\n

方法获取

    \n
  1. 导包
  2. \n
  3. 获取类对象
  4. \n
  5. 获取到方法—— getMethod
  6. \n
  7. 调用指定对象的方法—— invoke
  8. \n
\n
1
2
3
4
5
6
7
8
9
import java.lang.reflect.Method;

Hero h = new Hero();

// 获取这个名字叫做setName,参数类型是String的方法
Method m = h.getClass().getMethod("setName", String.class);

// 对h对象,调用这个方法
m.invoke(h, "garent");
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Java反射机制

获取类对象

类对象

类对象,用于描述这种类,都有什么属性,什么方法的

\n

获取类对象

    \n
  • Class.forName();

    \n
  • \n
  • Hero.class

    \n

    (注:Hero是自定义类的类名)

    \n
  • \n
  • new Hero().getClass()

    \n
  • \n
\n

在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。

\n
1
2
3
4
5
6
7
8
String className = "charactor.Hero";

Class pClass1 = Class.forName(className);
Class pClass2 = Hero.class;
Class pClass3 = new Hero().getClass();

System.out.println(pClass1 == pClass2);//true
System.out.println(pClass1 == pClass3);//true
\n

获取类对象,会导致类属性的初始化

无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。

\n

(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)

\n

创建类对象

使用反射机制创建类对象,3个步骤

\n
    \n
  1. 获取类对象
  2. \n
  3. 获取构造器
  4. \n
  5. 通过构造器实例化
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
//使用反射的方式创建对象
String className = "charactor.Hero";
//类对象
Class pClass = Class.forName(className);

//构造器
Constructor c = pClass.getConstructor();

//通过构造器实例化
Hero h2 = (Hero) c.newInstance();
\n

属性访问

用法

使用反射机制获取并修改类对象属性(也叫字段Field),4个步骤

\n
    \n
  1. 导包
  2. \n
  3. 获取类对象
  4. \n
  5. 获取属性(字段)—— getDeclaredField
  6. \n
  7. 修改指定对象的属性值—— set
  8. \n
\n
1
2
3
4
5
6
7
8
9
import java.lang.reflect.Field;

Hero h = new Hero();

Class c = h.getClass();

Field f = c.getDeclaredField("name");

f1.set(h, "teemo");//修改这个字段的值
\n

区分 getField 与 getDeclaredField

    \n
  • 这两个方法都是用于获取字段

    \n
  • \n
  • getField 只能获取public的,包括从父类继承来的字段。

    \n
  • \n
  • getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。

    \n

    : 这里只能获取到private的字段,但并不能访问该private字段的,除非加上setAccessible(true)

    \n
  • \n
\n

方法获取

    \n
  1. 导包
  2. \n
  3. 获取类对象
  4. \n
  5. 获取到方法—— getMethod
  6. \n
  7. 调用指定对象的方法—— invoke
  8. \n
\n
1
2
3
4
5
6
7
8
9
import java.lang.reflect.Method;

Hero h = new Hero();

// 获取这个名字叫做setName,参数类型是String的方法
Method m = h.getClass().getMethod("setName", String.class);

// 对h对象,调用这个方法
m.invoke(h, "garent");
\n"},{"title":"21-注解","abbrlink":"170e1590","date":"2021-02-20T08:42:36.000Z","description":"java的注解介绍与使用(元注解、自定义注解)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# Java注解Anotation\n\n## 什么是注解\n\n注解是jdk5.0开始引进的新技术\n\n\n\n### 作用是什么\n\n1. 注解的作用类似注释,但注释是给人(程序员)看的,而注解是给编译器看的。\n\n2. 注解通过标记包、类、字段、方法、局部变量、方法参数等元素据,告诉jvm这些元素据的信息\n\n\n\n### 格式\n\n\n\n以 `@注解名` 的格式存在,如:\n\n```java\n@override\n\n@suppressWarnings(value=\"unchecked\")\n```\n\n\n\n\n\n\n\n\n\n\n\n## 元注解\n\n\n\n元注解即用来定义注解的注解,也就是在你创建注解的时候会用到,Java有4个类型的元注解:\n\n```java\n@Target\n@Retention\n@Documented \n@Inherited\n```\n\n\n\n1. `@Target` 表示该注解用于什么地方,可以是包、类、方法......,在枚举类 `ElemenetType`中 有说明:\n\n ```java\n public enum ElementType {\n /** Class, interface (including annotation type), or enum declaration */\n TYPE,\n \n /** Field declaration (includes enum constants) */\n FIELD,\n \n /** Method declaration */\n METHOD,\n \n /** Formal parameter declaration */\n PARAMETER,\n \n /** Constructor declaration */\n CONSTRUCTOR,\n \n /** Local variable declaration */\n LOCAL_VARIABLE,\n \n /** Annotation type declaration */\n ANNOTATION_TYPE,\n \n /** Package declaration */\n PACKAGE,\n \n /**\n * Type parameter declaration\n *\n * @since 1.8\n */\n TYPE_PARAMETER,\n \n /**\n * Use of a type\n *\n * @since 1.8\n */\n TYPE_USE\n }\n ```\n\n 例如创建一个test1注解:\n\n ```java\n @Target({ElementType.METHOD, ElementType.TYPE}) //可以用在方法、class中\n @interface Test1{\n String value() default \"\"; //参数,默认为空 \n }\n ```\n\n \n\n2. `@Retention` 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:\n\n ```java\n RetentionPolicy.SOURCE //注解将被编译器丢弃 \n RetentionPolicy.CLASS //注解在class文件中可用,但会被VM丢弃 \n RetentionPolicy.RUNTIME \t\t//JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。通常自定义注解都用这个\n ```\n\n \n\n\n\n\n3. `@Documented` 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。\n\n\n\n4. `@Inherited` 允许子类继承父类中的注解。\n\n\n\n## 自定义注解\n\n一般使用元注解 \n\n- @Target,指明在哪里使用\n- @Retention, 指明在什么时候起作用\n\n例如:\n\n```java\n@Target({ElementType.METHOD, ElementType.TYPE})\n@Retention(value = RetentionPolicy.RUNTIME)\npublic@interface User{\n \n //下面是注解的参数,定义格式:“类型 参数名();” , 默认值看自己要不要写\n String name() default \"\"; //默认值为空\n int id() default -1; //默认值-1,代表不存在\n}\n```\n\n","source":"_posts/javaSE/21-注解.md","raw":"---\ntitle: 21-注解\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: '170e1590'\ndate: 2021-02-20 16:42:36\ndescription: java的注解介绍与使用(元注解、自定义注解)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# Java注解Anotation\n\n## 什么是注解\n\n注解是jdk5.0开始引进的新技术\n\n\n\n### 作用是什么\n\n1. 注解的作用类似注释,但注释是给人(程序员)看的,而注解是给编译器看的。\n\n2. 注解通过标记包、类、字段、方法、局部变量、方法参数等元素据,告诉jvm这些元素据的信息\n\n\n\n### 格式\n\n\n\n以 `@注解名` 的格式存在,如:\n\n```java\n@override\n\n@suppressWarnings(value=\"unchecked\")\n```\n\n\n\n\n\n\n\n\n\n\n\n## 元注解\n\n\n\n元注解即用来定义注解的注解,也就是在你创建注解的时候会用到,Java有4个类型的元注解:\n\n```java\n@Target\n@Retention\n@Documented \n@Inherited\n```\n\n\n\n1. `@Target` 表示该注解用于什么地方,可以是包、类、方法......,在枚举类 `ElemenetType`中 有说明:\n\n ```java\n public enum ElementType {\n /** Class, interface (including annotation type), or enum declaration */\n TYPE,\n \n /** Field declaration (includes enum constants) */\n FIELD,\n \n /** Method declaration */\n METHOD,\n \n /** Formal parameter declaration */\n PARAMETER,\n \n /** Constructor declaration */\n CONSTRUCTOR,\n \n /** Local variable declaration */\n LOCAL_VARIABLE,\n \n /** Annotation type declaration */\n ANNOTATION_TYPE,\n \n /** Package declaration */\n PACKAGE,\n \n /**\n * Type parameter declaration\n *\n * @since 1.8\n */\n TYPE_PARAMETER,\n \n /**\n * Use of a type\n *\n * @since 1.8\n */\n TYPE_USE\n }\n ```\n\n 例如创建一个test1注解:\n\n ```java\n @Target({ElementType.METHOD, ElementType.TYPE}) //可以用在方法、class中\n @interface Test1{\n String value() default \"\"; //参数,默认为空 \n }\n ```\n\n \n\n2. `@Retention` 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:\n\n ```java\n RetentionPolicy.SOURCE //注解将被编译器丢弃 \n RetentionPolicy.CLASS //注解在class文件中可用,但会被VM丢弃 \n RetentionPolicy.RUNTIME \t\t//JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。通常自定义注解都用这个\n ```\n\n \n\n\n\n\n3. `@Documented` 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。\n\n\n\n4. `@Inherited` 允许子类继承父类中的注解。\n\n\n\n## 自定义注解\n\n一般使用元注解 \n\n- @Target,指明在哪里使用\n- @Retention, 指明在什么时候起作用\n\n例如:\n\n```java\n@Target({ElementType.METHOD, ElementType.TYPE})\n@Retention(value = RetentionPolicy.RUNTIME)\npublic@interface User{\n \n //下面是注解的参数,定义格式:“类型 参数名();” , 默认值看自己要不要写\n String name() default \"\"; //默认值为空\n int id() default -1; //默认值-1,代表不存在\n}\n```\n\n","slug":"javaSE/21-注解","published":1,"updated":"2021-08-29T15:32:14.568Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qy006rakve1z853ljh","content":"

Java注解Anotation

什么是注解

注解是jdk5.0开始引进的新技术

\n

作用是什么

    \n
  1. 注解的作用类似注释,但注释是给人(程序员)看的,而注解是给编译器看的。

    \n
  2. \n
  3. 注解通过标记包、类、字段、方法、局部变量、方法参数等元素据,告诉jvm这些元素据的信息

    \n
  4. \n
\n

格式

@注解名 的格式存在,如:

\n
1
2
3
@override

@suppressWarnings(value="unchecked")
\n

元注解

元注解即用来定义注解的注解,也就是在你创建注解的时候会用到,Java有4个类型的元注解:

\n
1
2
3
4
@Target
@Retention
@Documented
@Inherited
\n
    \n
  1. @Target 表示该注解用于什么地方,可以是包、类、方法……,在枚举类 ElemenetType中 有说明:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
    * Type parameter declaration
    *
    * @since 1.8
    */
    TYPE_PARAMETER,

    /**
    * Use of a type
    *
    * @since 1.8
    */
    TYPE_USE
    }
    \n

    例如创建一个test1注解:

    \n
    1
    2
    3
    4
    @Target({ElementType.METHOD, ElementType.TYPE})     //可以用在方法、class中
    @interface Test1{
    String value() default ""; //参数,默认为空
    }
    \n
  2. \n
\n
    \n
  1. @Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:

    \n
    1
    2
    3
    RetentionPolicy.SOURCE        //注解将被编译器丢弃 
    RetentionPolicy.CLASS //注解在class文件中可用,但会被VM丢弃
    RetentionPolicy.RUNTIME \t\t//JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。通常自定义注解都用这个
    \n
  2. \n
\n
    \n
  1. @Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。
  2. \n
\n
    \n
  1. @Inherited 允许子类继承父类中的注解。
  2. \n
\n

自定义注解

一般使用元注解

\n
    \n
  • @Target,指明在哪里使用
  • \n
  • @Retention, 指明在什么时候起作用
  • \n
\n

例如:

\n
1
2
3
4
5
6
7
8
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public@interface User{

//下面是注解的参数,定义格式:“类型 参数名();” , 默认值看自己要不要写
String name() default ""; //默认值为空
int id() default -1; //默认值-1,代表不存在
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Java注解Anotation

什么是注解

注解是jdk5.0开始引进的新技术

\n

作用是什么

    \n
  1. 注解的作用类似注释,但注释是给人(程序员)看的,而注解是给编译器看的。

    \n
  2. \n
  3. 注解通过标记包、类、字段、方法、局部变量、方法参数等元素据,告诉jvm这些元素据的信息

    \n
  4. \n
\n

格式

@注解名 的格式存在,如:

\n
1
2
3
@override

@suppressWarnings(value="unchecked")
\n

元注解

元注解即用来定义注解的注解,也就是在你创建注解的时候会用到,Java有4个类型的元注解:

\n
1
2
3
4
@Target
@Retention
@Documented
@Inherited
\n
    \n
  1. @Target 表示该注解用于什么地方,可以是包、类、方法……,在枚举类 ElemenetType中 有说明:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
    * Type parameter declaration
    *
    * @since 1.8
    */
    TYPE_PARAMETER,

    /**
    * Use of a type
    *
    * @since 1.8
    */
    TYPE_USE
    }
    \n

    例如创建一个test1注解:

    \n
    1
    2
    3
    4
    @Target({ElementType.METHOD, ElementType.TYPE})     //可以用在方法、class中
    @interface Test1{
    String value() default ""; //参数,默认为空
    }
    \n
  2. \n
\n
    \n
  1. @Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:

    \n
    1
    2
    3
    RetentionPolicy.SOURCE        //注解将被编译器丢弃 
    RetentionPolicy.CLASS //注解在class文件中可用,但会被VM丢弃
    RetentionPolicy.RUNTIME \t\t//JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。通常自定义注解都用这个
    \n
  2. \n
\n
    \n
  1. @Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。
  2. \n
\n
    \n
  1. @Inherited 允许子类继承父类中的注解。
  2. \n
\n

自定义注解

一般使用元注解

\n
    \n
  • @Target,指明在哪里使用
  • \n
  • @Retention, 指明在什么时候起作用
  • \n
\n

例如:

\n
1
2
3
4
5
6
7
8
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public@interface User{

//下面是注解的参数,定义格式:“类型 参数名();” , 默认值看自己要不要写
String name() default ""; //默认值为空
int id() default -1; //默认值-1,代表不存在
}
\n"},{"title":"3.2-数组","abbrlink":"c63fec2a","date":"2021-02-09T05:20:22.000Z","description":"Java数组的常用操作函数","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# Arrays类\n\n## 常用数组操作\n\n| 操作 | 简介 |\n| :----------: | :------------------: |\n| copyOfRange | 拷贝数组 |\n| toString | 数组转为字符串 |\n| sort() | 数组排序 |\n| binarySearch | 搜索数组中的某一元素 |\n| equals | 判断是否相同 |\n| fill | 填充 |\n\n\n\n- `copyOfRange(int[] original, int from, int to)`\n - original表示源数组\n - from表示拷贝开始下标\n - to表示结束位置下标(不包含to位置)\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n\n int[] b = Arrays.copyOfRange(a, 0, 3); //0 <= i <3\n \n for (int i = 0; i < b.length; i++) {\n System.out.print(b[i] + \" \");\n }\n \n }\n //输出:18 62 68\n}\n```\n\n- `toString(a)`\n\n 将数组a转为字符串\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n String content = Arrays.toString(a);\n System.out.println(content);\n \n }\n}\n\n// 输出:[18, 62, 68, 82, 65, 9]\n```\n\n- `binarySearch(a, b)`\n - a为数组,b为该数组中的一个元素\n - 使用binarySearch进行查找之前,必须使用sort进行排序;\n 如果数组中有多个相同的元素,查找结果是不确定的\n - 注意输出位置从1开始,而不是0\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n \n Arrays.sort(a);//用binarySearch前,必须先使用sort进行排序\n \n System.out.println(\"数字 62 出现的位置:\"+Arrays.binarySearch(a, 62));\n }\n}\n\n//输出:\n//数字 62 出现的位置:2\n\n```\n\n- `equals(a1, a2)`\n - 判断a1, a2两个数组是否相同\n - 返回值为 `boolean`值\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n int b[] = new int[] { 18, 62, 68, 82, 65, 8 };\n \n System.out.println(Arrays.equals(a, b));\n }\n}\n\n//输出:false\n```\n\n- `fill(a, b)`\n - 往数组a中填充元素b(如果a已经有值,则a中的所有值会被替换为b)\n - 无返回值\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[10];\n \n Arrays.fill(a, 5);\n \n System.out.println(Arrays.toString(a));\n \n }\n}\n\n//输出:[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\n```\n\n","source":"_posts/javaSE/3.2-数组.md","raw":"---\ntitle: 3.2-数组\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: c63fec2a\ndate: 2021-02-09 13:20:22\ndescription: Java数组的常用操作函数\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# Arrays类\n\n## 常用数组操作\n\n| 操作 | 简介 |\n| :----------: | :------------------: |\n| copyOfRange | 拷贝数组 |\n| toString | 数组转为字符串 |\n| sort() | 数组排序 |\n| binarySearch | 搜索数组中的某一元素 |\n| equals | 判断是否相同 |\n| fill | 填充 |\n\n\n\n- `copyOfRange(int[] original, int from, int to)`\n - original表示源数组\n - from表示拷贝开始下标\n - to表示结束位置下标(不包含to位置)\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n\n int[] b = Arrays.copyOfRange(a, 0, 3); //0 <= i <3\n \n for (int i = 0; i < b.length; i++) {\n System.out.print(b[i] + \" \");\n }\n \n }\n //输出:18 62 68\n}\n```\n\n- `toString(a)`\n\n 将数组a转为字符串\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n String content = Arrays.toString(a);\n System.out.println(content);\n \n }\n}\n\n// 输出:[18, 62, 68, 82, 65, 9]\n```\n\n- `binarySearch(a, b)`\n - a为数组,b为该数组中的一个元素\n - 使用binarySearch进行查找之前,必须使用sort进行排序;\n 如果数组中有多个相同的元素,查找结果是不确定的\n - 注意输出位置从1开始,而不是0\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n \n Arrays.sort(a);//用binarySearch前,必须先使用sort进行排序\n \n System.out.println(\"数字 62 出现的位置:\"+Arrays.binarySearch(a, 62));\n }\n}\n\n//输出:\n//数字 62 出现的位置:2\n\n```\n\n- `equals(a1, a2)`\n - 判断a1, a2两个数组是否相同\n - 返回值为 `boolean`值\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n int b[] = new int[] { 18, 62, 68, 82, 65, 8 };\n \n System.out.println(Arrays.equals(a, b));\n }\n}\n\n//输出:false\n```\n\n- `fill(a, b)`\n - 往数组a中填充元素b(如果a已经有值,则a中的所有值会被替换为b)\n - 无返回值\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[10];\n \n Arrays.fill(a, 5);\n \n System.out.println(Arrays.toString(a));\n \n }\n}\n\n//输出:[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\n```\n\n","slug":"javaSE/3.2-数组","published":1,"updated":"2021-08-29T15:30:47.232Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qz006uakve9nqxfhzr","content":"

Arrays类

常用数组操作

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作简介
copyOfRange拷贝数组
toString数组转为字符串
sort()数组排序
binarySearch搜索数组中的某一元素
equals判断是否相同
fill填充
\n
\n
    \n
  • copyOfRange(int[] original, int from, int to)
      \n
    • original表示源数组
    • \n
    • from表示拷贝开始下标
    • \n
    • to表示结束位置下标(不包含to位置)
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };

int[] b = Arrays.copyOfRange(a, 0, 3); //0 <= i <3

for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + " ");
}

}
//输出:18 62 68
}
\n
    \n
  • toString(a)

    \n

    将数组a转为字符串

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
String content = Arrays.toString(a);
System.out.println(content);

}
}

// 输出:[18, 62, 68, 82, 65, 9]
\n
    \n
  • binarySearch(a, b)
      \n
    • a为数组,b为该数组中的一个元素
    • \n
    • 使用binarySearch进行查找之前,必须使用sort进行排序;
      如果数组中有多个相同的元素,查找结果是不确定的
    • \n
    • 注意输出位置从1开始,而不是0
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };

Arrays.sort(a);//用binarySearch前,必须先使用sort进行排序

System.out.println("数字 62 出现的位置:"+Arrays.binarySearch(a, 62));
}
}

//输出:
//数字 62 出现的位置:2

\n
    \n
  • equals(a1, a2)
      \n
    • 判断a1, a2两个数组是否相同
    • \n
    • 返回值为 boolean
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
int b[] = new int[] { 18, 62, 68, 82, 65, 8 };

System.out.println(Arrays.equals(a, b));
}
}

//输出:false
\n
    \n
  • fill(a, b)
      \n
    • 往数组a中填充元素b(如果a已经有值,则a中的所有值会被替换为b)
    • \n
    • 无返回值
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[10];

Arrays.fill(a, 5);

System.out.println(Arrays.toString(a));

}
}

//输出:[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Arrays类

常用数组操作

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作简介
copyOfRange拷贝数组
toString数组转为字符串
sort()数组排序
binarySearch搜索数组中的某一元素
equals判断是否相同
fill填充
\n
\n
    \n
  • copyOfRange(int[] original, int from, int to)
      \n
    • original表示源数组
    • \n
    • from表示拷贝开始下标
    • \n
    • to表示结束位置下标(不包含to位置)
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };

int[] b = Arrays.copyOfRange(a, 0, 3); //0 <= i <3

for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + " ");
}

}
//输出:18 62 68
}
\n
    \n
  • toString(a)

    \n

    将数组a转为字符串

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
String content = Arrays.toString(a);
System.out.println(content);

}
}

// 输出:[18, 62, 68, 82, 65, 9]
\n
    \n
  • binarySearch(a, b)
      \n
    • a为数组,b为该数组中的一个元素
    • \n
    • 使用binarySearch进行查找之前,必须使用sort进行排序;
      如果数组中有多个相同的元素,查找结果是不确定的
    • \n
    • 注意输出位置从1开始,而不是0
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };

Arrays.sort(a);//用binarySearch前,必须先使用sort进行排序

System.out.println("数字 62 出现的位置:"+Arrays.binarySearch(a, 62));
}
}

//输出:
//数字 62 出现的位置:2

\n
    \n
  • equals(a1, a2)
      \n
    • 判断a1, a2两个数组是否相同
    • \n
    • 返回值为 boolean
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
int b[] = new int[] { 18, 62, 68, 82, 65, 8 };

System.out.println(Arrays.equals(a, b));
}
}

//输出:false
\n
    \n
  • fill(a, b)
      \n
    • 往数组a中填充元素b(如果a已经有值,则a中的所有值会被替换为b)
    • \n
    • 无返回值
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[10];

Arrays.fill(a, 5);

System.out.println(Arrays.toString(a));

}
}

//输出:[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
\n"},{"title":"3.1-数据类型","abbrlink":"52da8eea","date":"2021-02-08T10:12:41.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","description":"java的数据类型分为基本数据类型和引用数据类型,基本的有数字类型(byte、short、int、long,float、double),字符型(char),布尔型(boolen);引用数据类型有类的引用、字符串(String)","_content":"\n\n\n\n# 数据类型\n\n## 基本数据类型\n\n### 整型 (4种)\n\n- byte (8位),最高位表示正负,$-2^7$~$2^7-1$\n\n byte a = 15;\n\n- short(16位),$-2^{15}$~$2^{15}-1$\n\n short a = 567;\n\n- int(32位),$-2^{31}$~$2^{31}-1$,默认为int型\n\n - 10进制:int a = 123;\n - 16进制:int a = 0x4f;\n - 8进制:int a = 0123;\n\n- long(64位),$-2^{63}$~$2^{63}-1$\n\n long a = 678L;\n\n```java\npublic class HelloWorld{\n public static void main(String[] args){\n byte b = 1;\n short s = 200;\n int i = 300;\n long l = 400;\n \n /*如果试图给byte类型的变量赋予超出其范围的值,就会产生编译错误*/\n byte b2 = 200; //报错\n }\n}\n```\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n long val = 26L; //以L结尾的字面值表示long型\n int decVal = 26; //默认就是int型\n int hexVal = 0x1a; //16进制\n int oxVal = 032; //8进制\n int binVal = 0b11010; //2进制\n }\n}\n```\n\n\n\n### 字符型 (1种)\n\n- `char`类型用于存放一个字符,值用单引号 `''` 表示 (双引号表示字符串);\n- 长度和short一样,也是16位的,**只能存放一个字符**,超过一个字符就会产生编译错误;\n- 中文字符与英文字母一样,都占用2个字节,即16位\n\n```java\npublic class HelloWorld{\n \n public static void main(String[] args){\n char c = '中';\n char A = 65; \n \n //char 只能存放一个字符,超过一个字符就会产生编译错误\n char c2 = '中国'; //报错\n char c3 = 'ab'; //报错\n \n }\n}\n```\n\n> char与short区别:\n>\n> ​\t在java中,char和short都是两个字节的长度。但char表示的是16位无符号整数,表示的范围是0~65535。short表示的是16位有符号整数,范围为-32768~32767。char用来保存一个Unicode编码的字符。char和short之间类型转换需要强转。\n\n### 浮点型 (2种)\n\n- float(32位)\n- double(64位),小数默认都是double类型,因此要声明float类型的数,要在末尾加一个字母`f`\n\n```java\npublic class HelloWorld{\n \n public static void main(String[] args){\n \n float f = 54.321;//报错,因为54.321是double型的\n float f2 = 54.321f;\n \n double d1 = 1.234;\n double d2 = 1.2e3 //即1.2*1000\n \n }\n}\n```\n\n### 布尔型(1种)\n\n- boolen(1位):用于表示真假,`true`或 `false`,注意不能用1或0来代替\n\n```java\npublic class HelloWorld {\n \n public static void main(String[] args) {\n \n boolean b1 = true;\n boolean b2 = false;\n \n // 虽然布尔型真正存放的数据是0(false) 1(true)\n // 但是,不能直接使用0 1 进行赋值\n boolean b3 = 1;\n \n }\n}\n```\n\n\n\n## 引用数据类型\n\n引用数据类型存储的是数据存放的地址\n\n### 类\n\n在Java中,可以创建一个类,再利用类来创建一个新的实例(对象),该实例的类型就是一种引用数据类型\n\n```java\npublic class Date{\n int year;\n int month;\n int day;\n}\n\n```\n\n```java\nDate d1 = new Date() //d1为引用数据类型\n```\n\n### 字符串(String)\n\nString将字符串当作一个整体来处理,不能修改串中的字符元素,可看作一个字符串常量。\n\n如`\"This is a string.\\n\"`。注意使用双引号。\n\n- 定义一个String:\n\n```java\nString s1 = new String(\"123abc\");\n\nString s2;\ns2 = \"123abc\";\n\nString s3 = \"123abc\";\n```\n\n- 连接两个字符串用 `+`\n\n```java\nString s1 = \"123\";\nString s2 = \"abc\";\nString s3;\ns3 = s1 + s2;\n```\n\n### 数组\n\n数组是一个**固定长度**的,包含了**相同类型**数据的 **容器**\n\n- #### 声明数组(不会创建数组)\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n // 声明一个数组\n int[] a;\n }\n}\n\n/*\nint[] a; 声明了一个数组变量。\n[]表示该变量是一个数组\nint 表示数组里的每一个元素都是一个整数\na 是变量名\n但是,仅仅是这一句声明,不会创建数组\n*/\n```\n\n- #### 创建数组\n\n```java\n//先声明再创建\nint[] a;\na = new int[5]\n```\n\n```java\n//声明的同时,创建一个数组\nint[] a = new int[5]; \n```\n\n数组名 `a`是一个引用\n\n- #### 初始化数组\n\n 如果数组的类型为int,未初始化时每个元素默认为0\n\n - 先声明,创建数组,再一个个元素进行赋值\n \n ```java\n int[] a = new int[5]; //声明和创建\n \n //没有赋值,那么就会使用默认值\n //作为int类型的数组,默认值是0\n System.out.println(a[0]);\n \n //进行赋值\n \n a[0] = 100;\n a[1] = 101;\n a[2] = 103;\n a[3] = 120;\n a[4] = 140;\n ```\n \n - 声明、创建的同时初始化\n \n ```java\n public class HelloWorld {\n public static void main(String[] args) {\n //写法一: 分配空间同时赋值\n int[] a = new int[]{100,102,444,836,3236};\n \n //写法二: 省略了new int[],效果一样\n int[] b = {100,102,444,836,3236};\n \n }\n }\n ```\n \n 注意:初始化的同时不能指定数组长度:\n \n ```java\n int[] c = new int[5]{100,102,444,836,3236};\n ```\n \n \n\n- #### 访问数组\n\n```java\na[0]=1;\na[1]=2;\na[2]=3;\n```\n\n- #### 数组长度\n\n `.length`属性用于访问一个数组的长度\n\n```java\nSystem.out.println(a.length)\n```\n\n- #### 二维数组\n\n 下面声明、创建并初始化了一个二维数组\n\n```java\nint[][] b = new int[][]{\n {1,2,3},\n {4,5,6},\n {7,8,9}\n};\n```\n\n","source":"_posts/javaSE/3.1-数据类型.md","raw":"---\ntitle: 3.1-数据类型\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 52da8eea\ndate: 2021-02-08 18:12:41\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\ndescription: java的数据类型分为基本数据类型和引用数据类型,基本的有数字类型(byte、short、int、long,float、double),字符型(char),布尔型(boolen);引用数据类型有类的引用、字符串(String)\n---\n\n\n\n\n# 数据类型\n\n## 基本数据类型\n\n### 整型 (4种)\n\n- byte (8位),最高位表示正负,$-2^7$~$2^7-1$\n\n byte a = 15;\n\n- short(16位),$-2^{15}$~$2^{15}-1$\n\n short a = 567;\n\n- int(32位),$-2^{31}$~$2^{31}-1$,默认为int型\n\n - 10进制:int a = 123;\n - 16进制:int a = 0x4f;\n - 8进制:int a = 0123;\n\n- long(64位),$-2^{63}$~$2^{63}-1$\n\n long a = 678L;\n\n```java\npublic class HelloWorld{\n public static void main(String[] args){\n byte b = 1;\n short s = 200;\n int i = 300;\n long l = 400;\n \n /*如果试图给byte类型的变量赋予超出其范围的值,就会产生编译错误*/\n byte b2 = 200; //报错\n }\n}\n```\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n long val = 26L; //以L结尾的字面值表示long型\n int decVal = 26; //默认就是int型\n int hexVal = 0x1a; //16进制\n int oxVal = 032; //8进制\n int binVal = 0b11010; //2进制\n }\n}\n```\n\n\n\n### 字符型 (1种)\n\n- `char`类型用于存放一个字符,值用单引号 `''` 表示 (双引号表示字符串);\n- 长度和short一样,也是16位的,**只能存放一个字符**,超过一个字符就会产生编译错误;\n- 中文字符与英文字母一样,都占用2个字节,即16位\n\n```java\npublic class HelloWorld{\n \n public static void main(String[] args){\n char c = '中';\n char A = 65; \n \n //char 只能存放一个字符,超过一个字符就会产生编译错误\n char c2 = '中国'; //报错\n char c3 = 'ab'; //报错\n \n }\n}\n```\n\n> char与short区别:\n>\n> ​\t在java中,char和short都是两个字节的长度。但char表示的是16位无符号整数,表示的范围是0~65535。short表示的是16位有符号整数,范围为-32768~32767。char用来保存一个Unicode编码的字符。char和short之间类型转换需要强转。\n\n### 浮点型 (2种)\n\n- float(32位)\n- double(64位),小数默认都是double类型,因此要声明float类型的数,要在末尾加一个字母`f`\n\n```java\npublic class HelloWorld{\n \n public static void main(String[] args){\n \n float f = 54.321;//报错,因为54.321是double型的\n float f2 = 54.321f;\n \n double d1 = 1.234;\n double d2 = 1.2e3 //即1.2*1000\n \n }\n}\n```\n\n### 布尔型(1种)\n\n- boolen(1位):用于表示真假,`true`或 `false`,注意不能用1或0来代替\n\n```java\npublic class HelloWorld {\n \n public static void main(String[] args) {\n \n boolean b1 = true;\n boolean b2 = false;\n \n // 虽然布尔型真正存放的数据是0(false) 1(true)\n // 但是,不能直接使用0 1 进行赋值\n boolean b3 = 1;\n \n }\n}\n```\n\n\n\n## 引用数据类型\n\n引用数据类型存储的是数据存放的地址\n\n### 类\n\n在Java中,可以创建一个类,再利用类来创建一个新的实例(对象),该实例的类型就是一种引用数据类型\n\n```java\npublic class Date{\n int year;\n int month;\n int day;\n}\n\n```\n\n```java\nDate d1 = new Date() //d1为引用数据类型\n```\n\n### 字符串(String)\n\nString将字符串当作一个整体来处理,不能修改串中的字符元素,可看作一个字符串常量。\n\n如`\"This is a string.\\n\"`。注意使用双引号。\n\n- 定义一个String:\n\n```java\nString s1 = new String(\"123abc\");\n\nString s2;\ns2 = \"123abc\";\n\nString s3 = \"123abc\";\n```\n\n- 连接两个字符串用 `+`\n\n```java\nString s1 = \"123\";\nString s2 = \"abc\";\nString s3;\ns3 = s1 + s2;\n```\n\n### 数组\n\n数组是一个**固定长度**的,包含了**相同类型**数据的 **容器**\n\n- #### 声明数组(不会创建数组)\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n // 声明一个数组\n int[] a;\n }\n}\n\n/*\nint[] a; 声明了一个数组变量。\n[]表示该变量是一个数组\nint 表示数组里的每一个元素都是一个整数\na 是变量名\n但是,仅仅是这一句声明,不会创建数组\n*/\n```\n\n- #### 创建数组\n\n```java\n//先声明再创建\nint[] a;\na = new int[5]\n```\n\n```java\n//声明的同时,创建一个数组\nint[] a = new int[5]; \n```\n\n数组名 `a`是一个引用\n\n- #### 初始化数组\n\n 如果数组的类型为int,未初始化时每个元素默认为0\n\n - 先声明,创建数组,再一个个元素进行赋值\n \n ```java\n int[] a = new int[5]; //声明和创建\n \n //没有赋值,那么就会使用默认值\n //作为int类型的数组,默认值是0\n System.out.println(a[0]);\n \n //进行赋值\n \n a[0] = 100;\n a[1] = 101;\n a[2] = 103;\n a[3] = 120;\n a[4] = 140;\n ```\n \n - 声明、创建的同时初始化\n \n ```java\n public class HelloWorld {\n public static void main(String[] args) {\n //写法一: 分配空间同时赋值\n int[] a = new int[]{100,102,444,836,3236};\n \n //写法二: 省略了new int[],效果一样\n int[] b = {100,102,444,836,3236};\n \n }\n }\n ```\n \n 注意:初始化的同时不能指定数组长度:\n \n ```java\n int[] c = new int[5]{100,102,444,836,3236};\n ```\n \n \n\n- #### 访问数组\n\n```java\na[0]=1;\na[1]=2;\na[2]=3;\n```\n\n- #### 数组长度\n\n `.length`属性用于访问一个数组的长度\n\n```java\nSystem.out.println(a.length)\n```\n\n- #### 二维数组\n\n 下面声明、创建并初始化了一个二维数组\n\n```java\nint[][] b = new int[][]{\n {1,2,3},\n {4,5,6},\n {7,8,9}\n};\n```\n\n","slug":"javaSE/3.1-数据类型","published":1,"updated":"2021-08-29T15:30:43.790Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r0006xakve9680bvvn","content":"

数据类型

基本数据类型

整型 (4种)

    \n
  • byte (8位),最高位表示正负,$-2^7$~$2^7-1$

    \n

    byte a = 15;

    \n
  • \n
  • short(16位),$-2^{15}$~$2^{15}-1$

    \n

    short a = 567;

    \n
  • \n
  • int(32位),$-2^{31}$~$2^{31}-1$,默认为int型

    \n
      \n
    • 10进制:int a = 123;
    • \n
    • 16进制:int a = 0x4f;
    • \n
    • 8进制:int a = 0123;
    • \n
    \n
  • \n
  • long(64位),$-2^{63}$~$2^{63}-1$

    \n

    long a = 678L;

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
public class HelloWorld{
public static void main(String[] args){
byte b = 1;
short s = 200;
int i = 300;
long l = 400;

/*如果试图给byte类型的变量赋予超出其范围的值,就会产生编译错误*/
byte b2 = 200; //报错
}
}
\n
1
2
3
4
5
6
7
8
9
public class HelloWorld {
public static void main(String[] args) {
long val = 26L; //以L结尾的字面值表示long型
int decVal = 26; //默认就是int型
int hexVal = 0x1a; //16进制
int oxVal = 032; //8进制
int binVal = 0b11010; //2进制
}
}
\n

字符型 (1种)

    \n
  • char类型用于存放一个字符,值用单引号 '' 表示 (双引号表示字符串);
  • \n
  • 长度和short一样,也是16位的,只能存放一个字符,超过一个字符就会产生编译错误;
  • \n
  • 中文字符与英文字母一样,都占用2个字节,即16位
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorld{

public static void main(String[] args){
char c = '中';
char A = 65;

//char 只能存放一个字符,超过一个字符就会产生编译错误
char c2 = '中国'; //报错
char c3 = 'ab'; //报错

}
}
\n
\n

char与short区别:

\n

​ 在java中,char和short都是两个字节的长度。但char表示的是16位无符号整数,表示的范围是0~65535。short表示的是16位有符号整数,范围为-32768~32767。char用来保存一个Unicode编码的字符。char和short之间类型转换需要强转。

\n
\n

浮点型 (2种)

    \n
  • float(32位)
  • \n
  • double(64位),小数默认都是double类型,因此要声明float类型的数,要在末尾加一个字母f
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorld{

public static void main(String[] args){

float f = 54.321;//报错,因为54.321是double型的
float f2 = 54.321f;

double d1 = 1.234;
double d2 = 1.2e3 //即1.2*1000

}
}
\n

布尔型(1种)

    \n
  • boolen(1位):用于表示真假,truefalse,注意不能用1或0来代替
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloWorld {

public static void main(String[] args) {

boolean b1 = true;
boolean b2 = false;

// 虽然布尔型真正存放的数据是0(false) 1(true)
// 但是,不能直接使用0 1 进行赋值
boolean b3 = 1;

}
}
\n

引用数据类型

引用数据类型存储的是数据存放的地址

\n

在Java中,可以创建一个类,再利用类来创建一个新的实例(对象),该实例的类型就是一种引用数据类型

\n
1
2
3
4
5
6
public class Date{
int year;
int month;
int day;
}

\n
1
Date d1 = new Date() //d1为引用数据类型
\n

字符串(String)

String将字符串当作一个整体来处理,不能修改串中的字符元素,可看作一个字符串常量。

\n

"This is a string.\\n"。注意使用双引号。

\n
    \n
  • 定义一个String:
  • \n
\n
1
2
3
4
5
6
String s1 = new String("123abc");

String s2;
s2 = "123abc";

String s3 = "123abc";
\n
    \n
  • 连接两个字符串用 +
  • \n
\n
1
2
3
4
String s1 = "123";
String s2 = "abc";
String s3;
s3 = s1 + s2;
\n

数组

数组是一个固定长度的,包含了相同类型数据的 容器

\n
    \n
  • 声明数组(不会创建数组)

  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class HelloWorld {
public static void main(String[] args) {
// 声明一个数组
int[] a;
}
}

/*
int[] a; 声明了一个数组变量。
[]表示该变量是一个数组
int 表示数组里的每一个元素都是一个整数
a 是变量名
但是,仅仅是这一句声明,不会创建数组
*/
\n
    \n
  • 创建数组

  • \n
\n
1
2
3
//先声明再创建
int[] a;
a = new int[5]
\n
1
2
//声明的同时,创建一个数组
int[] a = new int[5];
\n

数组名 a是一个引用

\n
    \n
  • 初始化数组

    如果数组的类型为int,未初始化时每个元素默认为0

    \n
      \n
    • 先声明,创建数组,再一个个元素进行赋值
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int[] a = new int[5]; //声明和创建

    //没有赋值,那么就会使用默认值
    //作为int类型的数组,默认值是0
    System.out.println(a[0]);

    //进行赋值

    a[0] = 100;
    a[1] = 101;
    a[2] = 103;
    a[3] = 120;
    a[4] = 140;
    \n
      \n
    • 声明、创建的同时初始化
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloWorld {
    public static void main(String[] args) {
    //写法一: 分配空间同时赋值
    int[] a = new int[]{100,102,444,836,3236};

    //写法二: 省略了new int[],效果一样
    int[] b = {100,102,444,836,3236};

    }
    }
    \n

    注意:初始化的同时不能指定数组长度:

    \n
    1
    int[] c = new int[5]{100,102,444,836,3236};
    \n
  • \n
\n
    \n
  • 访问数组

  • \n
\n
1
2
3
a[0]=1;
a[1]=2;
a[2]=3;
\n
    \n
  • 数组长度

    .length属性用于访问一个数组的长度

    \n
  • \n
\n
1
System.out.println(a.length)
\n
    \n
  • 二维数组

    下面声明、创建并初始化了一个二维数组

    \n
  • \n
\n
1
2
3
4
5
int[][] b = new int[][]{
{1,2,3},
{4,5,6},
{7,8,9}
};
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

数据类型

基本数据类型

整型 (4种)

    \n
  • byte (8位),最高位表示正负,$-2^7$~$2^7-1$

    \n

    byte a = 15;

    \n
  • \n
  • short(16位),$-2^{15}$~$2^{15}-1$

    \n

    short a = 567;

    \n
  • \n
  • int(32位),$-2^{31}$~$2^{31}-1$,默认为int型

    \n
      \n
    • 10进制:int a = 123;
    • \n
    • 16进制:int a = 0x4f;
    • \n
    • 8进制:int a = 0123;
    • \n
    \n
  • \n
  • long(64位),$-2^{63}$~$2^{63}-1$

    \n

    long a = 678L;

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
public class HelloWorld{
public static void main(String[] args){
byte b = 1;
short s = 200;
int i = 300;
long l = 400;

/*如果试图给byte类型的变量赋予超出其范围的值,就会产生编译错误*/
byte b2 = 200; //报错
}
}
\n
1
2
3
4
5
6
7
8
9
public class HelloWorld {
public static void main(String[] args) {
long val = 26L; //以L结尾的字面值表示long型
int decVal = 26; //默认就是int型
int hexVal = 0x1a; //16进制
int oxVal = 032; //8进制
int binVal = 0b11010; //2进制
}
}
\n

字符型 (1种)

    \n
  • char类型用于存放一个字符,值用单引号 '' 表示 (双引号表示字符串);
  • \n
  • 长度和short一样,也是16位的,只能存放一个字符,超过一个字符就会产生编译错误;
  • \n
  • 中文字符与英文字母一样,都占用2个字节,即16位
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorld{

public static void main(String[] args){
char c = '中';
char A = 65;

//char 只能存放一个字符,超过一个字符就会产生编译错误
char c2 = '中国'; //报错
char c3 = 'ab'; //报错

}
}
\n
\n

char与short区别:

\n

​ 在java中,char和short都是两个字节的长度。但char表示的是16位无符号整数,表示的范围是0~65535。short表示的是16位有符号整数,范围为-32768~32767。char用来保存一个Unicode编码的字符。char和short之间类型转换需要强转。

\n
\n

浮点型 (2种)

    \n
  • float(32位)
  • \n
  • double(64位),小数默认都是double类型,因此要声明float类型的数,要在末尾加一个字母f
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorld{

public static void main(String[] args){

float f = 54.321;//报错,因为54.321是double型的
float f2 = 54.321f;

double d1 = 1.234;
double d2 = 1.2e3 //即1.2*1000

}
}
\n

布尔型(1种)

    \n
  • boolen(1位):用于表示真假,truefalse,注意不能用1或0来代替
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloWorld {

public static void main(String[] args) {

boolean b1 = true;
boolean b2 = false;

// 虽然布尔型真正存放的数据是0(false) 1(true)
// 但是,不能直接使用0 1 进行赋值
boolean b3 = 1;

}
}
\n

引用数据类型

引用数据类型存储的是数据存放的地址

\n

在Java中,可以创建一个类,再利用类来创建一个新的实例(对象),该实例的类型就是一种引用数据类型

\n
1
2
3
4
5
6
public class Date{
int year;
int month;
int day;
}

\n
1
Date d1 = new Date() //d1为引用数据类型
\n

字符串(String)

String将字符串当作一个整体来处理,不能修改串中的字符元素,可看作一个字符串常量。

\n

"This is a string.\\n"。注意使用双引号。

\n
    \n
  • 定义一个String:
  • \n
\n
1
2
3
4
5
6
String s1 = new String("123abc");

String s2;
s2 = "123abc";

String s3 = "123abc";
\n
    \n
  • 连接两个字符串用 +
  • \n
\n
1
2
3
4
String s1 = "123";
String s2 = "abc";
String s3;
s3 = s1 + s2;
\n

数组

数组是一个固定长度的,包含了相同类型数据的 容器

\n
    \n
  • 声明数组(不会创建数组)

  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class HelloWorld {
public static void main(String[] args) {
// 声明一个数组
int[] a;
}
}

/*
int[] a; 声明了一个数组变量。
[]表示该变量是一个数组
int 表示数组里的每一个元素都是一个整数
a 是变量名
但是,仅仅是这一句声明,不会创建数组
*/
\n
    \n
  • 创建数组

  • \n
\n
1
2
3
//先声明再创建
int[] a;
a = new int[5]
\n
1
2
//声明的同时,创建一个数组
int[] a = new int[5];
\n

数组名 a是一个引用

\n
    \n
  • 初始化数组

    如果数组的类型为int,未初始化时每个元素默认为0

    \n
      \n
    • 先声明,创建数组,再一个个元素进行赋值
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int[] a = new int[5]; //声明和创建

    //没有赋值,那么就会使用默认值
    //作为int类型的数组,默认值是0
    System.out.println(a[0]);

    //进行赋值

    a[0] = 100;
    a[1] = 101;
    a[2] = 103;
    a[3] = 120;
    a[4] = 140;
    \n
      \n
    • 声明、创建的同时初始化
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloWorld {
    public static void main(String[] args) {
    //写法一: 分配空间同时赋值
    int[] a = new int[]{100,102,444,836,3236};

    //写法二: 省略了new int[],效果一样
    int[] b = {100,102,444,836,3236};

    }
    }
    \n

    注意:初始化的同时不能指定数组长度:

    \n
    1
    int[] c = new int[5]{100,102,444,836,3236};
    \n
  • \n
\n
    \n
  • 访问数组

  • \n
\n
1
2
3
a[0]=1;
a[1]=2;
a[2]=3;
\n
    \n
  • 数组长度

    .length属性用于访问一个数组的长度

    \n
  • \n
\n
1
System.out.println(a.length)
\n
    \n
  • 二维数组

    下面声明、创建并初始化了一个二维数组

    \n
  • \n
\n
1
2
3
4
5
int[][] b = new int[][]{
{1,2,3},
{4,5,6},
{7,8,9}
};
\n"},{"title":"5-操作符","abbrlink":"8a45e6a3","date":"2021-02-11T08:41:16.000Z","description":"java的运算符,基本运算、关系运算、逻辑运算、位运算","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 操作符\n\n## 算数操作符\n\n### 基本操作符:\n\n```java\n+ - * / %\n```\n\n\n\n### 5.1.2 任意运算单元的长度超过int\n\n如果有任何运算单元的长度超过int,那么运算结果就按照最长的长度计算\n比如:\n\n```java\nint a = 5;\nlong b = 6;\n// a+b 结果类型是long\n```\n\n### 任意运算单元的长度小于int\n\n如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算\n\n如:byte + byte -> int, short + short -> int\n\n\n\n### 自增、自减\n\n```java\n//以++为例\nint i = 5;\nint s;\ns = i++; //先取值,再运算,结果s的值即表达式的值,为5,i的值为6\n\n```\n\n```java\n以++为例\nint i = 5;\nint s;\ns = ++i; //先运算,再取值,结果s的值即表达式的值,为6,i的值为6\n\n```\n\n\n\n## 关系操作符\n\n比较两个变量之间关系,返回`true或false`\n\n```\n>\n<\n== 等于\n>=\n<=\n!= 不等于\n```\n\n\n\n## 逻辑操作符\n\n| 操作符 | 简称 | 意义 |\n| :----: | :----: | :---------------------------------------------------------: |\n| & | 长路与 | 无论第一个表达式的值是true或者false,第二个的值,都会被运算 |\n| && | 短路与 | 只要第一个表达式的值是false的,第二个表达式就不进行运算了 |\n| \\| | 长路或 | 符号两侧的表达式都被计算 |\n| \\|\\| | 短路或 | 只要第一个是true的,第二个就不进行运算了 |\n| ! | 取反 | 真的将变成假,假的将变成真 |\n| ^ | 异或 | 符号两边一真一假,返回真;两边同为真或同为假,返回假 |\n\n---\n| 操作符 | 同 | 异 |\n| :------: | :----------------------------: | :--------------------: |\n| &、&& | 运算符两边都为真时,结果才为真 | 第二个表达式是否被计算 |\n| \\|、\\|\\| | 运算符两边都为假时,结果才为假 | |\n\n\n\n\n\n## 位运算符\n\n| 运算符 | 简介 |\n| :----------------------: | :------------------: |\n| Integer.toBinaryString() | 一个整数的二进制表达 |\n| `|` | 位或 |\n| `&` | 位与 |\n| `^` | 异或 |\n| `~` | 取反 |\n| `<<` | 左移,即乘以$2^n$ |\n| `>>` | 右移 |\n\n\n\n## 三元操作符\n\n条件运算符`?:`是一个三元操作符\n\n用法:\n\n```java\n表达式?值1:值2\n如果表达式为真,返回值1\n如果表达式为假,返回值2\n```\n\n","source":"_posts/javaSE/5-操作符.md","raw":"---\ntitle: 5-操作符\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 8a45e6a3\ndate: 2021-02-11 16:41:16\ndescription: java的运算符,基本运算、关系运算、逻辑运算、位运算\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 操作符\n\n## 算数操作符\n\n### 基本操作符:\n\n```java\n+ - * / %\n```\n\n\n\n### 5.1.2 任意运算单元的长度超过int\n\n如果有任何运算单元的长度超过int,那么运算结果就按照最长的长度计算\n比如:\n\n```java\nint a = 5;\nlong b = 6;\n// a+b 结果类型是long\n```\n\n### 任意运算单元的长度小于int\n\n如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算\n\n如:byte + byte -> int, short + short -> int\n\n\n\n### 自增、自减\n\n```java\n//以++为例\nint i = 5;\nint s;\ns = i++; //先取值,再运算,结果s的值即表达式的值,为5,i的值为6\n\n```\n\n```java\n以++为例\nint i = 5;\nint s;\ns = ++i; //先运算,再取值,结果s的值即表达式的值,为6,i的值为6\n\n```\n\n\n\n## 关系操作符\n\n比较两个变量之间关系,返回`true或false`\n\n```\n>\n<\n== 等于\n>=\n<=\n!= 不等于\n```\n\n\n\n## 逻辑操作符\n\n| 操作符 | 简称 | 意义 |\n| :----: | :----: | :---------------------------------------------------------: |\n| & | 长路与 | 无论第一个表达式的值是true或者false,第二个的值,都会被运算 |\n| && | 短路与 | 只要第一个表达式的值是false的,第二个表达式就不进行运算了 |\n| \\| | 长路或 | 符号两侧的表达式都被计算 |\n| \\|\\| | 短路或 | 只要第一个是true的,第二个就不进行运算了 |\n| ! | 取反 | 真的将变成假,假的将变成真 |\n| ^ | 异或 | 符号两边一真一假,返回真;两边同为真或同为假,返回假 |\n\n---\n| 操作符 | 同 | 异 |\n| :------: | :----------------------------: | :--------------------: |\n| &、&& | 运算符两边都为真时,结果才为真 | 第二个表达式是否被计算 |\n| \\|、\\|\\| | 运算符两边都为假时,结果才为假 | |\n\n\n\n\n\n## 位运算符\n\n| 运算符 | 简介 |\n| :----------------------: | :------------------: |\n| Integer.toBinaryString() | 一个整数的二进制表达 |\n| `|` | 位或 |\n| `&` | 位与 |\n| `^` | 异或 |\n| `~` | 取反 |\n| `<<` | 左移,即乘以$2^n$ |\n| `>>` | 右移 |\n\n\n\n## 三元操作符\n\n条件运算符`?:`是一个三元操作符\n\n用法:\n\n```java\n表达式?值1:值2\n如果表达式为真,返回值1\n如果表达式为假,返回值2\n```\n\n","slug":"javaSE/5-操作符","published":1,"updated":"2021-08-29T15:30:53.821Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r10070akvedu2zcjwq","content":"

操作符

算数操作符

基本操作符:

1
+ - * / %
\n

5.1.2 任意运算单元的长度超过int

如果有任何运算单元的长度超过int,那么运算结果就按照最长的长度计算
比如:

\n
1
2
3
int a = 5;
long b = 6;
// a+b 结果类型是long
\n

任意运算单元的长度小于int

如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算

\n

如:byte + byte -> int, short + short -> int

\n

自增、自减

1
2
3
4
5
//以++为例
int i = 5;
int s;
s = i++; //先取值,再运算,结果s的值即表达式的值,为5,i的值为6

\n
1
2
3
4
5
以++为例
int i = 5;
int s;
s = ++i; //先运算,再取值,结果s的值即表达式的值,为6,i的值为6

\n

关系操作符

比较两个变量之间关系,返回true或false

\n
1
2
3
4
5
6
>
<
== 等于
>=
<=
!= 不等于
\n

逻辑操作符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作符简称意义
&长路与无论第一个表达式的值是true或者false,第二个的值,都会被运算
&&短路与只要第一个表达式的值是false的,第二个表达式就不进行运算了
\\长路或符号两侧的表达式都被计算
\\\\短路或只要第一个是true的,第二个就不进行运算了
!取反真的将变成假,假的将变成真
^异或符号两边一真一假,返回真;两边同为真或同为假,返回假
\n
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作符
&、&&运算符两边都为真时,结果才为真第二个表达式是否被计算
\\、\\\\运算符两边都为假时,结果才为假
\n
\n

位运算符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符简介
Integer.toBinaryString()一个整数的二进制表达
``位或
&位与
^异或
~取反
<<左移,即乘以$2^n$
>>右移
\n
\n

三元操作符

条件运算符?:是一个三元操作符

\n

用法:

\n
1
2
3
表达式?值1:值2
如果表达式为真,返回值1
如果表达式为假,返回值2
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

操作符

算数操作符

基本操作符:

1
+ - * / %
\n

5.1.2 任意运算单元的长度超过int

如果有任何运算单元的长度超过int,那么运算结果就按照最长的长度计算
比如:

\n
1
2
3
int a = 5;
long b = 6;
// a+b 结果类型是long
\n

任意运算单元的长度小于int

如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算

\n

如:byte + byte -> int, short + short -> int

\n

自增、自减

1
2
3
4
5
//以++为例
int i = 5;
int s;
s = i++; //先取值,再运算,结果s的值即表达式的值,为5,i的值为6

\n
1
2
3
4
5
以++为例
int i = 5;
int s;
s = ++i; //先运算,再取值,结果s的值即表达式的值,为6,i的值为6

\n

关系操作符

比较两个变量之间关系,返回true或false

\n
1
2
3
4
5
6
>
<
== 等于
>=
<=
!= 不等于
\n

逻辑操作符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作符简称意义
&长路与无论第一个表达式的值是true或者false,第二个的值,都会被运算
&&短路与只要第一个表达式的值是false的,第二个表达式就不进行运算了
\\长路或符号两侧的表达式都被计算
\\\\短路或只要第一个是true的,第二个就不进行运算了
!取反真的将变成假,假的将变成真
^异或符号两边一真一假,返回真;两边同为真或同为假,返回假
\n
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作符
&、&&运算符两边都为真时,结果才为真第二个表达式是否被计算
\\、\\\\运算符两边都为假时,结果才为假
\n
\n

位运算符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符简介
Integer.toBinaryString()一个整数的二进制表达
``位或
&位与
^异或
~取反
<<左移,即乘以$2^n$
>>右移
\n
\n

三元操作符

条件运算符?:是一个三元操作符

\n

用法:

\n
1
2
3
表达式?值1:值2
如果表达式为真,返回值1
如果表达式为假,返回值2
\n"},{"title":"4-变量","abbrlink":"68f2c599","date":"2021-02-10T06:55:37.000Z","description":"Java变量命名、类型转换、分类","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 变量\n\n## 命名规则\n\n- 变量名只能使用 `字母`、`数字`、`$`、`_`\n- 变量第一个字符 只能使用 `字母 、$、 _`\n- 使用完整单词命名\n\n\n\n## 类型转换\n\n- 低精度向高精度转换可以正常转换\n\n- 高精度向低精度转换有风险\n\n ```java\n public class HelloWorld {\n \n public static void main(String[] args) {\n \n byte b = 5;\n int i1 = 10;\n int i2 = 300;\n \n b = (byte) i1;\n //因为i1的值是在byte范围之内,所以即便进行强制转换\n //最后得到的值,也是10\n System.out.println(b);\n \n //因为i2的值是在byte范围之外,所以就会按照byte的长度进行截取\n //i2的值是300,其对应的二进制数是 100101100\n //按照byte的长度8位进行截取后,其值为 00101100 即44\n b =(byte) i2;\n System.out.println(b);\n \n //查看一个整数对应的二进制的方法:\n System.out.println(Integer.toBinaryString(i2));\n \n }\n }\n ```\n\n 注意:`short + short = int`\n\n## 变量分类\n\n- 成员变量(字段、属性、Field):\n\n 当一个变量被声明在类下面,变量就叫做**成员变量**,或**字段** 、**属性**、**Field**\n\n 比如变量下面例子中,`i`就是一个属性。那么从第2行这个变量声明的位置开始,整个类都可以访问得到;所以其作用域就是从其声明的位置开始的整个类\n\n ```java\n public class HelloWorld {\n int i = 1;\n int j = i; //其他的属性可以访问i\n public void method1(){\n System.out.println(i); //方法1里可以访问i\n }\n public void method2(){\n System.out.println(i); //方法2里可以访问i\n }\n }\n ```\n\n- 参数\n\n 如果一个变量,是声明在一个方法上的,就叫做**参数**,参数的作用域即为**该方法**内的所有代码,在该方法外面不能使用该参数\n\n ```java\n public class HelloWorld {\n \n public void method1(int i){ //参数i的作用域即方法method1\n System.out.println(i);\n }\n public void method2(){\n \n System.out.println(i); //method2 不能访问参数i\n }\n \n int j = i; //类里面也不能访问参数i\n }\n ```\n\n- 局部变量\n\n 声明在方法内的变量,叫做局部变量,其作用域在声明开始的位置,到其所处于的块结束位置。\n\n ```java\n public class HelloWorld {\n \n public void method1() {\n int i = 5; //其作用范围是从声明的第4行,到其所处于的块结束12行位置\n System.out.println(i);\n { //子块\n System.out.println(i); //可以访问i\n int j = 6;\n System.out.println(j); //可以访问j\n }\n System.out.println(j); //不能访问j,因为其作用域到第10行就结束了\n }\n \n }\n ```\n\n\n\n- 静态变量\n\n ```java\n static int age;\t//(类变量)\n ```\n\n \n\n## final变量\n\n用final修饰的变量,只能赋值1次,因此常**用于定义常量**\n\n```java\npublic class HelloWorld {\n \n public void method1() {\n final int i = 5;\n \n i = 10; //i在第4行已经被赋值过了,所以这里会出现编译错误\n \n }\n \n}\n```\n\n但是对于引用类型的数据,变量存储的是地址,地址不可变,但地址里存放的值可以改变\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n final int []a = {1,2,3};\n a[1] = 10;\n a[2] = 100;\n \n System.out.println(a[1]);\n System.out.println(a[2]);\n \n // 注意这样写:a = {4,5,6}; 是错误的\n\n }\n}\n```\n\n输出如下![image-20210122203958650](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210122203958650.png)\n\n\n\n\n\n\n\n","source":"_posts/javaSE/4-变量.md","raw":"---\ntitle: 4-变量\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 68f2c599\ndate: 2021-02-10 14:55:37\ndescription: Java变量命名、类型转换、分类\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 变量\n\n## 命名规则\n\n- 变量名只能使用 `字母`、`数字`、`$`、`_`\n- 变量第一个字符 只能使用 `字母 、$、 _`\n- 使用完整单词命名\n\n\n\n## 类型转换\n\n- 低精度向高精度转换可以正常转换\n\n- 高精度向低精度转换有风险\n\n ```java\n public class HelloWorld {\n \n public static void main(String[] args) {\n \n byte b = 5;\n int i1 = 10;\n int i2 = 300;\n \n b = (byte) i1;\n //因为i1的值是在byte范围之内,所以即便进行强制转换\n //最后得到的值,也是10\n System.out.println(b);\n \n //因为i2的值是在byte范围之外,所以就会按照byte的长度进行截取\n //i2的值是300,其对应的二进制数是 100101100\n //按照byte的长度8位进行截取后,其值为 00101100 即44\n b =(byte) i2;\n System.out.println(b);\n \n //查看一个整数对应的二进制的方法:\n System.out.println(Integer.toBinaryString(i2));\n \n }\n }\n ```\n\n 注意:`short + short = int`\n\n## 变量分类\n\n- 成员变量(字段、属性、Field):\n\n 当一个变量被声明在类下面,变量就叫做**成员变量**,或**字段** 、**属性**、**Field**\n\n 比如变量下面例子中,`i`就是一个属性。那么从第2行这个变量声明的位置开始,整个类都可以访问得到;所以其作用域就是从其声明的位置开始的整个类\n\n ```java\n public class HelloWorld {\n int i = 1;\n int j = i; //其他的属性可以访问i\n public void method1(){\n System.out.println(i); //方法1里可以访问i\n }\n public void method2(){\n System.out.println(i); //方法2里可以访问i\n }\n }\n ```\n\n- 参数\n\n 如果一个变量,是声明在一个方法上的,就叫做**参数**,参数的作用域即为**该方法**内的所有代码,在该方法外面不能使用该参数\n\n ```java\n public class HelloWorld {\n \n public void method1(int i){ //参数i的作用域即方法method1\n System.out.println(i);\n }\n public void method2(){\n \n System.out.println(i); //method2 不能访问参数i\n }\n \n int j = i; //类里面也不能访问参数i\n }\n ```\n\n- 局部变量\n\n 声明在方法内的变量,叫做局部变量,其作用域在声明开始的位置,到其所处于的块结束位置。\n\n ```java\n public class HelloWorld {\n \n public void method1() {\n int i = 5; //其作用范围是从声明的第4行,到其所处于的块结束12行位置\n System.out.println(i);\n { //子块\n System.out.println(i); //可以访问i\n int j = 6;\n System.out.println(j); //可以访问j\n }\n System.out.println(j); //不能访问j,因为其作用域到第10行就结束了\n }\n \n }\n ```\n\n\n\n- 静态变量\n\n ```java\n static int age;\t//(类变量)\n ```\n\n \n\n## final变量\n\n用final修饰的变量,只能赋值1次,因此常**用于定义常量**\n\n```java\npublic class HelloWorld {\n \n public void method1() {\n final int i = 5;\n \n i = 10; //i在第4行已经被赋值过了,所以这里会出现编译错误\n \n }\n \n}\n```\n\n但是对于引用类型的数据,变量存储的是地址,地址不可变,但地址里存放的值可以改变\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n final int []a = {1,2,3};\n a[1] = 10;\n a[2] = 100;\n \n System.out.println(a[1]);\n System.out.println(a[2]);\n \n // 注意这样写:a = {4,5,6}; 是错误的\n\n }\n}\n```\n\n输出如下![image-20210122203958650](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210122203958650.png)\n\n\n\n\n\n\n\n","slug":"javaSE/4-变量","published":1,"updated":"2021-08-29T15:30:50.603Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r20074akve5rp09eoj","content":"

变量

命名规则

    \n
  • 变量名只能使用 字母数字$_
  • \n
  • 变量第一个字符 只能使用 字母 、$、 _
  • \n
  • 使用完整单词命名
  • \n
\n

类型转换

    \n
  • 低精度向高精度转换可以正常转换

    \n
  • \n
  • 高精度向低精度转换有风险

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class HelloWorld {

    public static void main(String[] args) {

    byte b = 5;
    int i1 = 10;
    int i2 = 300;

    b = (byte) i1;
    //因为i1的值是在byte范围之内,所以即便进行强制转换
    //最后得到的值,也是10
    System.out.println(b);

    //因为i2的值是在byte范围之外,所以就会按照byte的长度进行截取
    //i2的值是300,其对应的二进制数是 100101100
    //按照byte的长度8位进行截取后,其值为 00101100 即44
    b =(byte) i2;
    System.out.println(b);

    //查看一个整数对应的二进制的方法:
    System.out.println(Integer.toBinaryString(i2));

    }
    }
    \n

    注意:short + short = int

    \n
  • \n
\n

变量分类

    \n
  • 成员变量(字段、属性、Field):

    \n

    当一个变量被声明在类下面,变量就叫做成员变量,或字段属性Field

    \n

    比如变量下面例子中,i就是一个属性。那么从第2行这个变量声明的位置开始,整个类都可以访问得到;所以其作用域就是从其声明的位置开始的整个类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloWorld {
    int i = 1;
    int j = i; //其他的属性可以访问i
    public void method1(){
    System.out.println(i); //方法1里可以访问i
    }
    public void method2(){
    System.out.println(i); //方法2里可以访问i
    }
    }
    \n
  • \n
  • 参数

    \n

    如果一个变量,是声明在一个方法上的,就叫做参数,参数的作用域即为该方法内的所有代码,在该方法外面不能使用该参数

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class HelloWorld {

    public void method1(int i){ //参数i的作用域即方法method1
    System.out.println(i);
    }
    public void method2(){

    System.out.println(i); //method2 不能访问参数i
    }

    int j = i; //类里面也不能访问参数i
    }
    \n
  • \n
  • 局部变量

    \n

    声明在方法内的变量,叫做局部变量,其作用域在声明开始的位置,到其所处于的块结束位置。

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class HelloWorld {

    public void method1() {
    int i = 5; //其作用范围是从声明的第4行,到其所处于的块结束12行位置
    System.out.println(i);
    { //子块
    System.out.println(i); //可以访问i
    int j = 6;
    System.out.println(j); //可以访问j
    }
    System.out.println(j); //不能访问j,因为其作用域到第10行就结束了
    }

    }
    \n
  • \n
\n
    \n
  • 静态变量

    \n
    1
    static int age;\t//(类变量)
    \n
  • \n
\n

final变量

用final修饰的变量,只能赋值1次,因此常用于定义常量

\n
1
2
3
4
5
6
7
8
9
10
public class HelloWorld {

public void method1() {
final int i = 5;

i = 10; //i在第4行已经被赋值过了,所以这里会出现编译错误

}

}
\n

但是对于引用类型的数据,变量存储的是地址,地址不可变,但地址里存放的值可以改变

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloWorld {
public static void main(String[] args) {
final int []a = {1,2,3};
a[1] = 10;
a[2] = 100;

System.out.println(a[1]);
System.out.println(a[2]);

// 注意这样写:a = {4,5,6}; 是错误的

}
}
\n

输出如下\"image-20210122203958650\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

变量

命名规则

    \n
  • 变量名只能使用 字母数字$_
  • \n
  • 变量第一个字符 只能使用 字母 、$、 _
  • \n
  • 使用完整单词命名
  • \n
\n

类型转换

    \n
  • 低精度向高精度转换可以正常转换

    \n
  • \n
  • 高精度向低精度转换有风险

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class HelloWorld {

    public static void main(String[] args) {

    byte b = 5;
    int i1 = 10;
    int i2 = 300;

    b = (byte) i1;
    //因为i1的值是在byte范围之内,所以即便进行强制转换
    //最后得到的值,也是10
    System.out.println(b);

    //因为i2的值是在byte范围之外,所以就会按照byte的长度进行截取
    //i2的值是300,其对应的二进制数是 100101100
    //按照byte的长度8位进行截取后,其值为 00101100 即44
    b =(byte) i2;
    System.out.println(b);

    //查看一个整数对应的二进制的方法:
    System.out.println(Integer.toBinaryString(i2));

    }
    }
    \n

    注意:short + short = int

    \n
  • \n
\n

变量分类

    \n
  • 成员变量(字段、属性、Field):

    \n

    当一个变量被声明在类下面,变量就叫做成员变量,或字段属性Field

    \n

    比如变量下面例子中,i就是一个属性。那么从第2行这个变量声明的位置开始,整个类都可以访问得到;所以其作用域就是从其声明的位置开始的整个类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloWorld {
    int i = 1;
    int j = i; //其他的属性可以访问i
    public void method1(){
    System.out.println(i); //方法1里可以访问i
    }
    public void method2(){
    System.out.println(i); //方法2里可以访问i
    }
    }
    \n
  • \n
  • 参数

    \n

    如果一个变量,是声明在一个方法上的,就叫做参数,参数的作用域即为该方法内的所有代码,在该方法外面不能使用该参数

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class HelloWorld {

    public void method1(int i){ //参数i的作用域即方法method1
    System.out.println(i);
    }
    public void method2(){

    System.out.println(i); //method2 不能访问参数i
    }

    int j = i; //类里面也不能访问参数i
    }
    \n
  • \n
  • 局部变量

    \n

    声明在方法内的变量,叫做局部变量,其作用域在声明开始的位置,到其所处于的块结束位置。

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class HelloWorld {

    public void method1() {
    int i = 5; //其作用范围是从声明的第4行,到其所处于的块结束12行位置
    System.out.println(i);
    { //子块
    System.out.println(i); //可以访问i
    int j = 6;
    System.out.println(j); //可以访问j
    }
    System.out.println(j); //不能访问j,因为其作用域到第10行就结束了
    }

    }
    \n
  • \n
\n
    \n
  • 静态变量

    \n
    1
    static int age;\t//(类变量)
    \n
  • \n
\n

final变量

用final修饰的变量,只能赋值1次,因此常用于定义常量

\n
1
2
3
4
5
6
7
8
9
10
public class HelloWorld {

public void method1() {
final int i = 5;

i = 10; //i在第4行已经被赋值过了,所以这里会出现编译错误

}

}
\n

但是对于引用类型的数据,变量存储的是地址,地址不可变,但地址里存放的值可以改变

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloWorld {
public static void main(String[] args) {
final int []a = {1,2,3};
a[1] = 10;
a[2] = 100;

System.out.println(a[1]);
System.out.println(a[2]);

// 注意这样写:a = {4,5,6}; 是错误的

}
}
\n

输出如下\"image-20210122203958650\"

\n"},{"title":"6-输入语句","abbrlink":"63036c3c","date":"2021-02-12T05:37:38.000Z","description":"java输入语句Scanner的使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 输入语句\n\njava使用 `Scanner`语句进行输入操作,使用前需在前面加上:\n\n```java\nimport java.util.Scanner;\n```\n\n\n\n## 输入一个整数\n\n包括两个步骤:\n\n- 进入输入状态\n- 读取输入的指定类型的数据\n\n```java\nScanner s = new Scanner(System.in); //进入读取状态\nint a = s.nextInt(); //读取数据\n```\n\n\n\n## 输入一个浮点数\n\n```java\nScanner s = new Scanner(System.in);\nfloat b = s.nextFloat()\n```\n\n\n\n## 输入一个字符串\n\n```java\nScanner s = new Scanner(System.in);\nString s1 = s.nextLine();\nString s2 = s.next(); \n```\n\n\n\n## next()与nextLine()区别\n\nnext从第一个非空格字符开始读取,遇到空格就会停止(读取的数据不含空格),返回的是一个字符串\n\nnextLine读取一整行数据,遇到回车就停止(读取的数据不包含回车),返回的是一个字符串\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n public static void main(String[] args) {\n Scanner in = new Scanner(System.in);\n \n String s1 = in.next();\n String s2 = in.nextLine();\n \n System.out.print(s1);\n System.out.print(s2.length());\n System.out.print(\"===================\");\n }\n}\n```\n\n按下面格式输入:\n\n```\nabc\ndef\n```\n\n输出如下:\n\n```\nabc0===================\n```\n\n说明:`abc`是s1的内容,`0`是s2的长度(即s2什么也没得到)\n\n> next(),nextInt(),nextFloat()......这些只读取内容(不包含空格)部分,遇到空格或回车就截止了,把空格或回车放到了缓冲区\n>\n> nextLine()则是读取一整行,返回字符串,遇到回车(不读取回车)就截止\n>\n> 因此,如果在next()后使用nextLine(),在输入数据后,按下回车,则数据会被next()获取,\n>\n> **此时nextLine()紧接着进行读取,正好遇到回车,因此,nextLine什么也没得到。**\n","source":"_posts/javaSE/6-输入语句.md","raw":"---\ntitle: 6-输入语句\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 63036c3c\ndate: 2021-02-12 13:37:38\ndescription: java输入语句Scanner的使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 输入语句\n\njava使用 `Scanner`语句进行输入操作,使用前需在前面加上:\n\n```java\nimport java.util.Scanner;\n```\n\n\n\n## 输入一个整数\n\n包括两个步骤:\n\n- 进入输入状态\n- 读取输入的指定类型的数据\n\n```java\nScanner s = new Scanner(System.in); //进入读取状态\nint a = s.nextInt(); //读取数据\n```\n\n\n\n## 输入一个浮点数\n\n```java\nScanner s = new Scanner(System.in);\nfloat b = s.nextFloat()\n```\n\n\n\n## 输入一个字符串\n\n```java\nScanner s = new Scanner(System.in);\nString s1 = s.nextLine();\nString s2 = s.next(); \n```\n\n\n\n## next()与nextLine()区别\n\nnext从第一个非空格字符开始读取,遇到空格就会停止(读取的数据不含空格),返回的是一个字符串\n\nnextLine读取一整行数据,遇到回车就停止(读取的数据不包含回车),返回的是一个字符串\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n public static void main(String[] args) {\n Scanner in = new Scanner(System.in);\n \n String s1 = in.next();\n String s2 = in.nextLine();\n \n System.out.print(s1);\n System.out.print(s2.length());\n System.out.print(\"===================\");\n }\n}\n```\n\n按下面格式输入:\n\n```\nabc\ndef\n```\n\n输出如下:\n\n```\nabc0===================\n```\n\n说明:`abc`是s1的内容,`0`是s2的长度(即s2什么也没得到)\n\n> next(),nextInt(),nextFloat()......这些只读取内容(不包含空格)部分,遇到空格或回车就截止了,把空格或回车放到了缓冲区\n>\n> nextLine()则是读取一整行,返回字符串,遇到回车(不读取回车)就截止\n>\n> 因此,如果在next()后使用nextLine(),在输入数据后,按下回车,则数据会被next()获取,\n>\n> **此时nextLine()紧接着进行读取,正好遇到回车,因此,nextLine什么也没得到。**\n","slug":"javaSE/6-输入语句","published":1,"updated":"2021-08-29T15:30:57.135Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r30078akvehflj72w7","content":"

输入语句

java使用 Scanner语句进行输入操作,使用前需在前面加上:

\n
1
import java.util.Scanner;
\n

输入一个整数

包括两个步骤:

\n
    \n
  • 进入输入状态
  • \n
  • 读取输入的指定类型的数据
  • \n
\n
1
2
Scanner s = new Scanner(System.in);      //进入读取状态
int a = s.nextInt(); //读取数据
\n

输入一个浮点数

1
2
Scanner s = new Scanner(System.in);
float b = s.nextFloat()
\n

输入一个字符串

1
2
3
Scanner s = new Scanner(System.in);
String s1 = s.nextLine();
String s2 = s.next();
\n

next()与nextLine()区别

next从第一个非空格字符开始读取,遇到空格就会停止(读取的数据不含空格),返回的是一个字符串

\n

nextLine读取一整行数据,遇到回车就停止(读取的数据不包含回车),返回的是一个字符串

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);

String s1 = in.next();
String s2 = in.nextLine();

System.out.print(s1);
System.out.print(s2.length());
System.out.print("===================");
}
}
\n

按下面格式输入:

\n
1
2
abc
def
\n

输出如下:

\n
1
abc0===================
\n

说明:abc是s1的内容,0是s2的长度(即s2什么也没得到)

\n
\n

next(),nextInt(),nextFloat()……这些只读取内容(不包含空格)部分,遇到空格或回车就截止了,把空格或回车放到了缓冲区

\n

nextLine()则是读取一整行,返回字符串,遇到回车(不读取回车)就截止

\n

因此,如果在next()后使用nextLine(),在输入数据后,按下回车,则数据会被next()获取,

\n

此时nextLine()紧接着进行读取,正好遇到回车,因此,nextLine什么也没得到。

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

输入语句

java使用 Scanner语句进行输入操作,使用前需在前面加上:

\n
1
import java.util.Scanner;
\n

输入一个整数

包括两个步骤:

\n
    \n
  • 进入输入状态
  • \n
  • 读取输入的指定类型的数据
  • \n
\n
1
2
Scanner s = new Scanner(System.in);      //进入读取状态
int a = s.nextInt(); //读取数据
\n

输入一个浮点数

1
2
Scanner s = new Scanner(System.in);
float b = s.nextFloat()
\n

输入一个字符串

1
2
3
Scanner s = new Scanner(System.in);
String s1 = s.nextLine();
String s2 = s.next();
\n

next()与nextLine()区别

next从第一个非空格字符开始读取,遇到空格就会停止(读取的数据不含空格),返回的是一个字符串

\n

nextLine读取一整行数据,遇到回车就停止(读取的数据不包含回车),返回的是一个字符串

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);

String s1 = in.next();
String s2 = in.nextLine();

System.out.print(s1);
System.out.print(s2.length());
System.out.print("===================");
}
}
\n

按下面格式输入:

\n
1
2
abc
def
\n

输出如下:

\n
1
abc0===================
\n

说明:abc是s1的内容,0是s2的长度(即s2什么也没得到)

\n
\n

next(),nextInt(),nextFloat()……这些只读取内容(不包含空格)部分,遇到空格或回车就截止了,把空格或回车放到了缓冲区

\n

nextLine()则是读取一整行,返回字符串,遇到回车(不读取回车)就截止

\n

因此,如果在next()后使用nextLine(),在输入数据后,按下回车,则数据会被next()获取,

\n

此时nextLine()紧接着进行读取,正好遇到回车,因此,nextLine什么也没得到。

\n
\n"},{"title":"7-条件语句","abbrlink":"b54f36d8","date":"2021-02-12T07:20:47.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","description":"java条件语句if...else..., switch","_content":"\n# 条件语句\n\n## if语句\n\n- 3种基本用法\n\n ```java\n ----------1------------\n if(){\n \n }\n ----------2------------\n if(){\n \n }\n else{\n \n }\n ----------3------------\n \n if(){\n \n }\n else if(){\n \n }\n else if(){\n \n }\n \n else{\n \n }\n -----------------------\n ```\n\n## switch语句\n\n- switch可以使用`byte,short,int,char,String,enum`\n- 每个表达式结束,都应该有一个break\n- String在Java1.7之前是不支持的, Java从1.7开始支持switch用String的,编译后是把String转化为hash值,其实还是整数\n- enum是枚举类型\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n \n int day = 5;\n\n switch(day){\n case 1:\n System.out.println(\"星期一\");\n break;\n case 2:\n System.out.println(\"星期二\");\n break;\n case 3:\n System.out.println(\"星期三\");\n break;\n case 4:\n System.out.println(\"星期四\");\n break;\n case 5:\n System.out.println(\"星期五\");\n break;\n case 6:\n System.out.println(\"星期六\");\n break;\n case 7:\n System.out.println(\"星期天\");\n break;\n default:\n System.out.println(\"这个是什么鬼?\");\n }\n \n }\n}\n```\n\n","source":"_posts/javaSE/7-条件语句.md","raw":"---\ntitle: 7-条件语句\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: b54f36d8\ndate: 2021-02-12 15:20:47\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\ndescription: java条件语句if...else..., switch\n---\n\n# 条件语句\n\n## if语句\n\n- 3种基本用法\n\n ```java\n ----------1------------\n if(){\n \n }\n ----------2------------\n if(){\n \n }\n else{\n \n }\n ----------3------------\n \n if(){\n \n }\n else if(){\n \n }\n else if(){\n \n }\n \n else{\n \n }\n -----------------------\n ```\n\n## switch语句\n\n- switch可以使用`byte,short,int,char,String,enum`\n- 每个表达式结束,都应该有一个break\n- String在Java1.7之前是不支持的, Java从1.7开始支持switch用String的,编译后是把String转化为hash值,其实还是整数\n- enum是枚举类型\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n \n int day = 5;\n\n switch(day){\n case 1:\n System.out.println(\"星期一\");\n break;\n case 2:\n System.out.println(\"星期二\");\n break;\n case 3:\n System.out.println(\"星期三\");\n break;\n case 4:\n System.out.println(\"星期四\");\n break;\n case 5:\n System.out.println(\"星期五\");\n break;\n case 6:\n System.out.println(\"星期六\");\n break;\n case 7:\n System.out.println(\"星期天\");\n break;\n default:\n System.out.println(\"这个是什么鬼?\");\n }\n \n }\n}\n```\n\n","slug":"javaSE/7-条件语句","published":1,"updated":"2021-08-30T08:29:26.817Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r5007cakve2yiob0j3","content":"

条件语句

if语句

    \n
  • 3种基本用法

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    ----------1------------
    if(){

    }
    ----------2------------
    if(){

    }
    else{

    }
    ----------3------------

    if(){

    }
    else if(){

    }
    else if(){

    }

    else{

    }
    -----------------------
    \n
  • \n
\n

switch语句

    \n
  • switch可以使用byte,short,int,char,String,enum
  • \n
  • 每个表达式结束,都应该有一个break
  • \n
  • String在Java1.7之前是不支持的, Java从1.7开始支持switch用String的,编译后是把String转化为hash值,其实还是整数
  • \n
  • enum是枚举类型
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class HelloWorld {
public static void main(String[] args) {

int day = 5;

switch(day){
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
case 4:
System.out.println("星期四");
break;
case 5:
System.out.println("星期五");
break;
case 6:
System.out.println("星期六");
break;
case 7:
System.out.println("星期天");
break;
default:
System.out.println("这个是什么鬼?");
}

}
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

条件语句

if语句

    \n
  • 3种基本用法

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    ----------1------------
    if(){

    }
    ----------2------------
    if(){

    }
    else{

    }
    ----------3------------

    if(){

    }
    else if(){

    }
    else if(){

    }

    else{

    }
    -----------------------
    \n
  • \n
\n

switch语句

    \n
  • switch可以使用byte,short,int,char,String,enum
  • \n
  • 每个表达式结束,都应该有一个break
  • \n
  • String在Java1.7之前是不支持的, Java从1.7开始支持switch用String的,编译后是把String转化为hash值,其实还是整数
  • \n
  • enum是枚举类型
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class HelloWorld {
public static void main(String[] args) {

int day = 5;

switch(day){
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
case 4:
System.out.println("星期四");
break;
case 5:
System.out.println("星期五");
break;
case 6:
System.out.println("星期六");
break;
case 7:
System.out.println("星期天");
break;
default:
System.out.println("这个是什么鬼?");
}

}
}
\n"},{"title":"9-package包","abbrlink":"94566522","date":"2021-02-13T09:18:41.000Z","description":"java中package使用介绍,可结合Java访问修饰符学习","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 包package\n\n- 通常会把比较接近的类,规划在同一个包下;\n\n- 在最开始的地方声明该类所处于的包名;\n\n ```java\n package charactor; //在最开始的地方声明该类所处于的包名\n public class Hero {\n \n String name; //姓名\n float hp; //血量\n int moveSpeed; //移动速度\n \n }\n ```\n\n \n\n- 使用同一个包下的其他类,直接使用即可\n\n- 使用其他包下的类,必须`import`\n\n![把比较接近的类,规划在同一个包下](https://stepimagewm.how2j.cn/600.png)\n\n\n\n```java\npackage charactor;\n \n//Weapon类在其他包里,使用必须进行import\nimport property.Weapon;\n \npublic class Hero {\n \n \n}\n```\n\n","source":"_posts/javaSE/9-package包.md","raw":"---\ntitle: 9-package包\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: '94566522'\ndate: 2021-02-13 17:18:41\ndescription: java中package使用介绍,可结合Java访问修饰符学习\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 包package\n\n- 通常会把比较接近的类,规划在同一个包下;\n\n- 在最开始的地方声明该类所处于的包名;\n\n ```java\n package charactor; //在最开始的地方声明该类所处于的包名\n public class Hero {\n \n String name; //姓名\n float hp; //血量\n int moveSpeed; //移动速度\n \n }\n ```\n\n \n\n- 使用同一个包下的其他类,直接使用即可\n\n- 使用其他包下的类,必须`import`\n\n![把比较接近的类,规划在同一个包下](https://stepimagewm.how2j.cn/600.png)\n\n\n\n```java\npackage charactor;\n \n//Weapon类在其他包里,使用必须进行import\nimport property.Weapon;\n \npublic class Hero {\n \n \n}\n```\n\n","slug":"javaSE/9-package包","published":1,"updated":"2021-08-29T15:31:14.685Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r5007fakve7bba4t62","content":"

包package

    \n
  • 通常会把比较接近的类,规划在同一个包下;

    \n
  • \n
  • 在最开始的地方声明该类所处于的包名;

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    package charactor; //在最开始的地方声明该类所处于的包名
    public class Hero {

    String name; //姓名
    float hp; //血量
    int moveSpeed; //移动速度

    }
    \n
  • \n
\n
    \n
  • 使用同一个包下的其他类,直接使用即可

    \n
  • \n
  • 使用其他包下的类,必须import

    \n
  • \n
\n

\"把比较接近的类,规划在同一个包下\"

\n
1
2
3
4
5
6
7
8
9
package charactor;

//Weapon类在其他包里,使用必须进行import
import property.Weapon;

public class Hero {


}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

包package

    \n
  • 通常会把比较接近的类,规划在同一个包下;

    \n
  • \n
  • 在最开始的地方声明该类所处于的包名;

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    package charactor; //在最开始的地方声明该类所处于的包名
    public class Hero {

    String name; //姓名
    float hp; //血量
    int moveSpeed; //移动速度

    }
    \n
  • \n
\n
    \n
  • 使用同一个包下的其他类,直接使用即可

    \n
  • \n
  • 使用其他包下的类,必须import

    \n
  • \n
\n

\"把比较接近的类,规划在同一个包下\"

\n
1
2
3
4
5
6
7
8
9
package charactor;

//Weapon类在其他包里,使用必须进行import
import property.Weapon;

public class Hero {


}
\n"},{"title":"win10任务栏透明 - win10","description":"Win10任务栏设置透明","cover":"https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png","abbrlink":"3de0f4a2","date":"2021-09-06T04:21:11.000Z","swiper_index":null,"_content":"\n\n\n### Win10使任务栏完全透明的方法\n\n1、桌面鼠标右键点击个性化,颜色》》选择颜色》》选择深色或自定义,自定义可按如下设置。\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H216041O53.jpg)\n\n\n\n2、win+R 输入regedit(也就是[注册表])注意以管理员身份运行!\n\n\n\n3、定位到以下目录\n\n`计算机\\HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced`\n\n右键该目录,选择新建→DWORD (32位) 值。\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160441G1.jpg)\n\n4、重命名此文件为TaskbarAcrylicOpacity。\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160449332.jpg)\n\n\n\n5、双击文件名称,然后按照红圈设置。0为完全透明,10为完全不透明,中间的值可以自己调整以达到适合的透明度.\n\n注意基数选择【十进制】!然后点击确定。\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H216045V50.jpg)\n\n\n\n6、重启Windows资源管理器,或者重启电脑,效果:\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160505S1.jpg)","source":"_posts/win10/win10任务栏透明.md","raw":"---\ntitle: win10任务栏透明 - win10\ncategories: win10\ndescription: Win10任务栏设置透明\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png'\nabbrlink: 3de0f4a2\ndate: 2021-09-06 12:21:11\nswiper_index:\n---\n\n\n\n### Win10使任务栏完全透明的方法\n\n1、桌面鼠标右键点击个性化,颜色》》选择颜色》》选择深色或自定义,自定义可按如下设置。\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H216041O53.jpg)\n\n\n\n2、win+R 输入regedit(也就是[注册表])注意以管理员身份运行!\n\n\n\n3、定位到以下目录\n\n`计算机\\HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced`\n\n右键该目录,选择新建→DWORD (32位) 值。\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160441G1.jpg)\n\n4、重命名此文件为TaskbarAcrylicOpacity。\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160449332.jpg)\n\n\n\n5、双击文件名称,然后按照红圈设置。0为完全透明,10为完全不透明,中间的值可以自己调整以达到适合的透明度.\n\n注意基数选择【十进制】!然后点击确定。\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H216045V50.jpg)\n\n\n\n6、重启Windows资源管理器,或者重启电脑,效果:\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160505S1.jpg)","slug":"win10/win10任务栏透明","published":1,"updated":"2021-09-07T14:29:17.913Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r6007jakvegi6bdbyo","content":"

Win10使任务栏完全透明的方法

1、桌面鼠标右键点击个性化,颜色》》选择颜色》》选择深色或自定义,自定义可按如下设置。

\n

\"Win10如何使任务栏完全透明?\"

\n

2、win+R 输入regedit(也就是[注册表])注意以管理员身份运行!

\n

3、定位到以下目录

\n

计算机\\HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced

\n

右键该目录,选择新建→DWORD (32位) 值。

\n

\"Win10如何使任务栏完全透明?\"

\n

4、重命名此文件为TaskbarAcrylicOpacity。

\n

\"Win10如何使任务栏完全透明?\"

\n

5、双击文件名称,然后按照红圈设置。0为完全透明,10为完全不透明,中间的值可以自己调整以达到适合的透明度.

\n

注意基数选择【十进制】!然后点击确定。

\n

\"Win10如何使任务栏完全透明?\"

\n

6、重启Windows资源管理器,或者重启电脑,效果:

\n

\"Win10如何使任务栏完全透明?\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Win10使任务栏完全透明的方法

1、桌面鼠标右键点击个性化,颜色》》选择颜色》》选择深色或自定义,自定义可按如下设置。

\n

\"Win10如何使任务栏完全透明?\"

\n

2、win+R 输入regedit(也就是[注册表])注意以管理员身份运行!

\n

3、定位到以下目录

\n

计算机\\HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced

\n

右键该目录,选择新建→DWORD (32位) 值。

\n

\"Win10如何使任务栏完全透明?\"

\n

4、重命名此文件为TaskbarAcrylicOpacity。

\n

\"Win10如何使任务栏完全透明?\"

\n

5、双击文件名称,然后按照红圈设置。0为完全透明,10为完全不透明,中间的值可以自己调整以达到适合的透明度.

\n

注意基数选择【十进制】!然后点击确定。

\n

\"Win10如何使任务栏完全透明?\"

\n

6、重启Windows资源管理器,或者重启电脑,效果:

\n

\"Win10如何使任务栏完全透明?\"

\n"},{"title":"8-循环语句","abbrlink":"753dfa3e","date":"2021-02-12T06:46:38.000Z","description":"while、do while、for、增强型for、continue、break","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 循环语句\n\n## while与 do while\n\n| 语句 | 含义 |\n| :------: | :-----------------------------------: |\n| while | 条件为true时 重复执行 |\n| do while | 条件为true时 重复执行,至少会执行一次 |\n\n用法:\n\n```java\n//while:\nwhile(){\n \n}\n-------------------------\n//do while\ndo{\n \n}while();\n```\n\n## for语句与增强型for语句\n\n### for语句\n\n用法(类似c/c++语言):\n\n```java\nfor(语句1;语句2;语句3){\n\t语句4;\n}\n```\n\n### 增强型for语句 \n\nJava5引入了一种主要用于**数组**的增强型for循环,用法如下:\n\n```java\nfor(局部变量:表达式){\n\n}\n\n/* 局部变量:该变量的类型必须和数组元素的类型匹配。\n * 其作用域限定在循环语句块,其值与此时数组元素的值相等。*/\n\n// 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。\n```\n\n- **声明语句:**声明新的局部变量,该**变量的类型必须和数组元素的类型匹配**。其作用域限定在循环语句块,其值与此时数组元素的值相等。\n\n- **表达式:**表达式是要访问的数组名,或者是**返回值为数组**的**方法**。\n\n## contiue语句\n\n满足条件时跳过本次循环,进入下一次循环\n\n## break语句\n\n满足条件时终止**距离最近**的一层循环;\n\n## 使用标签结束外部循环\n\n注意标签放的位置:需要终止的循环外部\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n \n //打印单数 \n outloop: //outloop这个标签可以自定义,比如a,b,c,outloop1...,放在需要终止的循环外部\n for (int i = 0; i < 10; i++) {\n \n for (int j = 0; j < 10; j++) {\n System.out.println(i+\":\"+j);\n if(0==j%2) \n break outloop; //如果是双数,结束外部循环\n }\n \n }\n \n }\n}\n```\n\n","source":"_posts/javaSE/8-循环语句.md","raw":"---\ntitle: 8-循环语句\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 753dfa3e\ndate: 2021-02-12 14:46:38\ndescription: while、do while、for、增强型for、continue、break\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 循环语句\n\n## while与 do while\n\n| 语句 | 含义 |\n| :------: | :-----------------------------------: |\n| while | 条件为true时 重复执行 |\n| do while | 条件为true时 重复执行,至少会执行一次 |\n\n用法:\n\n```java\n//while:\nwhile(){\n \n}\n-------------------------\n//do while\ndo{\n \n}while();\n```\n\n## for语句与增强型for语句\n\n### for语句\n\n用法(类似c/c++语言):\n\n```java\nfor(语句1;语句2;语句3){\n\t语句4;\n}\n```\n\n### 增强型for语句 \n\nJava5引入了一种主要用于**数组**的增强型for循环,用法如下:\n\n```java\nfor(局部变量:表达式){\n\n}\n\n/* 局部变量:该变量的类型必须和数组元素的类型匹配。\n * 其作用域限定在循环语句块,其值与此时数组元素的值相等。*/\n\n// 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。\n```\n\n- **声明语句:**声明新的局部变量,该**变量的类型必须和数组元素的类型匹配**。其作用域限定在循环语句块,其值与此时数组元素的值相等。\n\n- **表达式:**表达式是要访问的数组名,或者是**返回值为数组**的**方法**。\n\n## contiue语句\n\n满足条件时跳过本次循环,进入下一次循环\n\n## break语句\n\n满足条件时终止**距离最近**的一层循环;\n\n## 使用标签结束外部循环\n\n注意标签放的位置:需要终止的循环外部\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n \n //打印单数 \n outloop: //outloop这个标签可以自定义,比如a,b,c,outloop1...,放在需要终止的循环外部\n for (int i = 0; i < 10; i++) {\n \n for (int j = 0; j < 10; j++) {\n System.out.println(i+\":\"+j);\n if(0==j%2) \n break outloop; //如果是双数,结束外部循环\n }\n \n }\n \n }\n}\n```\n\n","slug":"javaSE/8-循环语句","published":1,"updated":"2021-08-29T15:31:11.601Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r7007lakve2vjjaimr","content":"

循环语句

while与 do while

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
语句含义
while条件为true时 重复执行
do while条件为true时 重复执行,至少会执行一次
\n
\n

用法:

\n
1
2
3
4
5
6
7
8
9
//while:
while(){

}
-------------------------
//do while
do{

}while();
\n

for语句与增强型for语句

for语句

用法(类似c/c++语言):

\n
1
2
3
for(语句1;语句2;语句3){
\t语句4;
}
\n

增强型for语句

Java5引入了一种主要用于数组的增强型for循环,用法如下:

\n
1
2
3
4
5
6
7
8
for(局部变量:表达式){

}

/* 局部变量:该变量的类型必须和数组元素的类型匹配。
* 其作用域限定在循环语句块,其值与此时数组元素的值相等。*/

// 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
\n
    \n
  • 声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。

    \n
  • \n
  • 表达式:表达式是要访问的数组名,或者是返回值为数组方法

    \n
  • \n
\n

contiue语句

满足条件时跳过本次循环,进入下一次循环

\n

break语句

满足条件时终止距离最近的一层循环;

\n

使用标签结束外部循环

注意标签放的位置:需要终止的循环外部

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloWorld {
public static void main(String[] args) {

//打印单数
outloop: //outloop这个标签可以自定义,比如a,b,c,outloop1...,放在需要终止的循环外部
for (int i = 0; i < 10; i++) {

for (int j = 0; j < 10; j++) {
System.out.println(i+":"+j);
if(0==j%2)
break outloop; //如果是双数,结束外部循环
}

}

}
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

循环语句

while与 do while

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
语句含义
while条件为true时 重复执行
do while条件为true时 重复执行,至少会执行一次
\n
\n

用法:

\n
1
2
3
4
5
6
7
8
9
//while:
while(){

}
-------------------------
//do while
do{

}while();
\n

for语句与增强型for语句

for语句

用法(类似c/c++语言):

\n
1
2
3
for(语句1;语句2;语句3){
\t语句4;
}
\n

增强型for语句

Java5引入了一种主要用于数组的增强型for循环,用法如下:

\n
1
2
3
4
5
6
7
8
for(局部变量:表达式){

}

/* 局部变量:该变量的类型必须和数组元素的类型匹配。
* 其作用域限定在循环语句块,其值与此时数组元素的值相等。*/

// 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
\n
    \n
  • 声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。

    \n
  • \n
  • 表达式:表达式是要访问的数组名,或者是返回值为数组方法

    \n
  • \n
\n

contiue语句

满足条件时跳过本次循环,进入下一次循环

\n

break语句

满足条件时终止距离最近的一层循环;

\n

使用标签结束外部循环

注意标签放的位置:需要终止的循环外部

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloWorld {
public static void main(String[] args) {

//打印单数
outloop: //outloop这个标签可以自定义,比如a,b,c,outloop1...,放在需要终止的循环外部
for (int i = 0; i < 10; i++) {

for (int j = 0; j < 10; j++) {
System.out.println(i+":"+j);
if(0==j%2)
break outloop; //如果是双数,结束外部循环
}

}

}
}
\n"},{"title":"win10主题美化","description":"Win10系统美化记录","cover":"https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png","abbrlink":"54a4218","swiper_index":4,"date":"2021-09-05T06:20:22.000Z","_content":"\n\n\n\n\n\n\n### 主题安装\n\n#### 主题破解补丁安装\n\n下载 [主题破解补丁](https://zhutix.com/tools/win-theme-patcher/) 到桌面,在“UltraUXThemePatcher.exe”上右键 – 以管理员身份运行(必须),点击Next,并一路同意直到安装成功,重启电脑。\n\n\n\n#### 安装主题\n\n打开你下载的主题压缩包,如果无法打开请先安装一款解压软件,比如 [WinRAR](https://zhutix.com/software/winrar-ha/) 或 [360压缩](https://yasuo.360.cn/),在里面找到主题安装文件,一般在“主题”文件夹内,或“***_TW10.exe”类型的文件,运行,不要改变安装路径(**绝对不要**),安装;完成后依次在桌面上 > 右键 > 个性化 > [主题](ms-settings:themes),能看到刚安装的主题样式,鼠标左键点击即可应用。\n\n\n\n#### 调整开始菜单及任务栏样式\n\n下载 [开始菜单程序](https://zhutix.com/tools/startisback-plus-pojie/) 到桌面,右键 > 以管理员身份运行“StartIsBack.exe”,根据需要选择“为我安装”或“为所有人安装”,不要改变安装路径,安装;完成后**在开始按钮处右键 > 属性,以打开配置面板**,在外观里面可以更换开始菜单及任务栏的样式、透明度等…\n\n\n\n\n\n### 图标\n\n#### 修改系统图标\n\n系统图标就是Win10系统自带的那些图标,比如文件夹、硬盘、控制面板等图标,不包括第三方软件的哦,像QQ、微信、酷狗之类的软件图标,不在此项,前后对比见下图:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_220614.jpg)\n\n目前致美化有两种图标包:iPack([素材](https://zhutix.com/tag/ipack/) · [教程](https://zhutix.com/study/ipack-ff/))、7tsp([素材](https://zhutix.com/tag/7tsp/) · [教程](https://zhutix.com/tools/7tsp-gui/)),可以一次性更改90%的系统图标,用途及效果虽然一样,但两种图标包的使用方法和兼容性不相同,使用前必须查阅相应教程;其中iPack图标包为.exe安装程序,兼容全部Win10系统,但风险较高;7tsp图标包为.7z文件,需要借助[7TSP GUI](https://zhutix.com/tools/7tsp-gui/)加载,仅兼容Win10 1903之后的系统,风险较低。\n\n很多主题,作者都是搭配好图标包的,有些直接包含在了主题压缩包内,有些是要下载的,以 [陶瓷灰](https://zhutix.com/pc/porcelain-vs/) 这款Win10主题的图标为例,我们打开主题压缩包:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_221330.jpg)\n\n你能看到里面有一个“图标.url”,双击打开它,即可在浏览器里面加载到图标下载页面,下载后,打开图标压缩包:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_221717.jpg)\n\n里面包含了“7tsp”和“iPack”文件夹,我们选择其中一种即可;再次说明,不论你选择的是哪一种,都必须先看下相应的教程再安装:[iPack图标包安装注意事项](https://zhutix.com/study/ipack-ff/)\n\n#### 修改桌面软件图标\n\n软件图标就是前面提到的像QQ、微信之类的啦:\n\n![img](https://dl.zhutix.net/2019/10/zhuomico-01.png)\n\n桌面上的图标分为两类:系统工具图标,如:此电脑、网络、文档、回收站;软件快捷方式图标,如:QQ、微信、360浏览器等…详细方法请参考:[桌面图标更改方法](https://zhutix.com/study/zhuomian-ruanjian/)\n\n\n\n\n\n#### 修改任务栏软件图标\n\n![img](https://dl.zhutix.net/2020/07/456234.jpg)\n\n任务栏软件图标,就是被你手动固定在任务栏上的那些软件的图标,如上图,详细方法请参考:[任务栏软件图标修改方法](https://zhutix.com/study/taskbar-icon-c/)\n\n![img](https://dl.zhutix.net/2017/03/rwlczcxtbxgff06.jpg?x-oss-process=image/resize,m_fill,h_120,w_168)\n\n![img](https://dl.zhutix.net/2017/03/rwlczcxtbxgff06.jpg?x-oss-process=image/resize,m_fill,h_120,w_168)\n\n\n\n\n\n","source":"_posts/win10/win10主题美化.md","raw":"---\ntitle: win10主题美化\ntags:\n - win10\ncategories: win10\ndescription: Win10系统美化记录\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png'\nabbrlink: 54a4218\nswiper_index: 4\ndate: 2021-09-05 14:20:22\n---\n\n\n\n\n\n\n\n### 主题安装\n\n#### 主题破解补丁安装\n\n下载 [主题破解补丁](https://zhutix.com/tools/win-theme-patcher/) 到桌面,在“UltraUXThemePatcher.exe”上右键 – 以管理员身份运行(必须),点击Next,并一路同意直到安装成功,重启电脑。\n\n\n\n#### 安装主题\n\n打开你下载的主题压缩包,如果无法打开请先安装一款解压软件,比如 [WinRAR](https://zhutix.com/software/winrar-ha/) 或 [360压缩](https://yasuo.360.cn/),在里面找到主题安装文件,一般在“主题”文件夹内,或“***_TW10.exe”类型的文件,运行,不要改变安装路径(**绝对不要**),安装;完成后依次在桌面上 > 右键 > 个性化 > [主题](ms-settings:themes),能看到刚安装的主题样式,鼠标左键点击即可应用。\n\n\n\n#### 调整开始菜单及任务栏样式\n\n下载 [开始菜单程序](https://zhutix.com/tools/startisback-plus-pojie/) 到桌面,右键 > 以管理员身份运行“StartIsBack.exe”,根据需要选择“为我安装”或“为所有人安装”,不要改变安装路径,安装;完成后**在开始按钮处右键 > 属性,以打开配置面板**,在外观里面可以更换开始菜单及任务栏的样式、透明度等…\n\n\n\n\n\n### 图标\n\n#### 修改系统图标\n\n系统图标就是Win10系统自带的那些图标,比如文件夹、硬盘、控制面板等图标,不包括第三方软件的哦,像QQ、微信、酷狗之类的软件图标,不在此项,前后对比见下图:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_220614.jpg)\n\n目前致美化有两种图标包:iPack([素材](https://zhutix.com/tag/ipack/) · [教程](https://zhutix.com/study/ipack-ff/))、7tsp([素材](https://zhutix.com/tag/7tsp/) · [教程](https://zhutix.com/tools/7tsp-gui/)),可以一次性更改90%的系统图标,用途及效果虽然一样,但两种图标包的使用方法和兼容性不相同,使用前必须查阅相应教程;其中iPack图标包为.exe安装程序,兼容全部Win10系统,但风险较高;7tsp图标包为.7z文件,需要借助[7TSP GUI](https://zhutix.com/tools/7tsp-gui/)加载,仅兼容Win10 1903之后的系统,风险较低。\n\n很多主题,作者都是搭配好图标包的,有些直接包含在了主题压缩包内,有些是要下载的,以 [陶瓷灰](https://zhutix.com/pc/porcelain-vs/) 这款Win10主题的图标为例,我们打开主题压缩包:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_221330.jpg)\n\n你能看到里面有一个“图标.url”,双击打开它,即可在浏览器里面加载到图标下载页面,下载后,打开图标压缩包:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_221717.jpg)\n\n里面包含了“7tsp”和“iPack”文件夹,我们选择其中一种即可;再次说明,不论你选择的是哪一种,都必须先看下相应的教程再安装:[iPack图标包安装注意事项](https://zhutix.com/study/ipack-ff/)\n\n#### 修改桌面软件图标\n\n软件图标就是前面提到的像QQ、微信之类的啦:\n\n![img](https://dl.zhutix.net/2019/10/zhuomico-01.png)\n\n桌面上的图标分为两类:系统工具图标,如:此电脑、网络、文档、回收站;软件快捷方式图标,如:QQ、微信、360浏览器等…详细方法请参考:[桌面图标更改方法](https://zhutix.com/study/zhuomian-ruanjian/)\n\n\n\n\n\n#### 修改任务栏软件图标\n\n![img](https://dl.zhutix.net/2020/07/456234.jpg)\n\n任务栏软件图标,就是被你手动固定在任务栏上的那些软件的图标,如上图,详细方法请参考:[任务栏软件图标修改方法](https://zhutix.com/study/taskbar-icon-c/)\n\n![img](https://dl.zhutix.net/2017/03/rwlczcxtbxgff06.jpg?x-oss-process=image/resize,m_fill,h_120,w_168)\n\n![img](https://dl.zhutix.net/2017/03/rwlczcxtbxgff06.jpg?x-oss-process=image/resize,m_fill,h_120,w_168)\n\n\n\n\n\n","slug":"win10/win10主题美化","published":1,"updated":"2021-09-05T09:32:32.290Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r8007qakvebtub8ep8","content":"

主题安装

主题破解补丁安装

下载 主题破解补丁 到桌面,在“UltraUXThemePatcher.exe”上右键 – 以管理员身份运行(必须),点击Next,并一路同意直到安装成功,重启电脑。

\n

安装主题

打开你下载的主题压缩包,如果无法打开请先安装一款解压软件,比如 WinRAR360压缩,在里面找到主题安装文件,一般在“主题”文件夹内,或“*_TW10.exe”类型的文件,运行,不要改变安装路径(绝对不要**),安装;完成后依次在桌面上 > 右键 > 个性化 > 主题,能看到刚安装的主题样式,鼠标左键点击即可应用。

\n

调整开始菜单及任务栏样式

下载 开始菜单程序 到桌面,右键 > 以管理员身份运行“StartIsBack.exe”,根据需要选择“为我安装”或“为所有人安装”,不要改变安装路径,安装;完成后在开始按钮处右键 > 属性,以打开配置面板,在外观里面可以更换开始菜单及任务栏的样式、透明度等…

\n

图标

修改系统图标

系统图标就是Win10系统自带的那些图标,比如文件夹、硬盘、控制面板等图标,不包括第三方软件的哦,像QQ、微信、酷狗之类的软件图标,不在此项,前后对比见下图:

\n

\"img\"

\n

目前致美化有两种图标包:iPack(素材 · 教程)、7tsp(素材 · 教程),可以一次性更改90%的系统图标,用途及效果虽然一样,但两种图标包的使用方法和兼容性不相同,使用前必须查阅相应教程;其中iPack图标包为.exe安装程序,兼容全部Win10系统,但风险较高;7tsp图标包为.7z文件,需要借助7TSP GUI加载,仅兼容Win10 1903之后的系统,风险较低。

\n

很多主题,作者都是搭配好图标包的,有些直接包含在了主题压缩包内,有些是要下载的,以 陶瓷灰 这款Win10主题的图标为例,我们打开主题压缩包:

\n

\"img\"

\n

你能看到里面有一个“图标.url”,双击打开它,即可在浏览器里面加载到图标下载页面,下载后,打开图标压缩包:

\n

\"img\"

\n

里面包含了“7tsp”和“iPack”文件夹,我们选择其中一种即可;再次说明,不论你选择的是哪一种,都必须先看下相应的教程再安装:iPack图标包安装注意事项

\n

修改桌面软件图标

软件图标就是前面提到的像QQ、微信之类的啦:

\n

\"img\"

\n

桌面上的图标分为两类:系统工具图标,如:此电脑、网络、文档、回收站;软件快捷方式图标,如:QQ、微信、360浏览器等…详细方法请参考:桌面图标更改方法

\n

修改任务栏软件图标

\"img\"

\n

任务栏软件图标,就是被你手动固定在任务栏上的那些软件的图标,如上图,详细方法请参考:任务栏软件图标修改方法

\n

\"img\"

\n

\"img\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

主题安装

主题破解补丁安装

下载 主题破解补丁 到桌面,在“UltraUXThemePatcher.exe”上右键 – 以管理员身份运行(必须),点击Next,并一路同意直到安装成功,重启电脑。

\n

安装主题

打开你下载的主题压缩包,如果无法打开请先安装一款解压软件,比如 WinRAR360压缩,在里面找到主题安装文件,一般在“主题”文件夹内,或“*_TW10.exe”类型的文件,运行,不要改变安装路径(绝对不要**),安装;完成后依次在桌面上 > 右键 > 个性化 > 主题,能看到刚安装的主题样式,鼠标左键点击即可应用。

\n

调整开始菜单及任务栏样式

下载 开始菜单程序 到桌面,右键 > 以管理员身份运行“StartIsBack.exe”,根据需要选择“为我安装”或“为所有人安装”,不要改变安装路径,安装;完成后在开始按钮处右键 > 属性,以打开配置面板,在外观里面可以更换开始菜单及任务栏的样式、透明度等…

\n

图标

修改系统图标

系统图标就是Win10系统自带的那些图标,比如文件夹、硬盘、控制面板等图标,不包括第三方软件的哦,像QQ、微信、酷狗之类的软件图标,不在此项,前后对比见下图:

\n

\"img\"

\n

目前致美化有两种图标包:iPack(素材 · 教程)、7tsp(素材 · 教程),可以一次性更改90%的系统图标,用途及效果虽然一样,但两种图标包的使用方法和兼容性不相同,使用前必须查阅相应教程;其中iPack图标包为.exe安装程序,兼容全部Win10系统,但风险较高;7tsp图标包为.7z文件,需要借助7TSP GUI加载,仅兼容Win10 1903之后的系统,风险较低。

\n

很多主题,作者都是搭配好图标包的,有些直接包含在了主题压缩包内,有些是要下载的,以 陶瓷灰 这款Win10主题的图标为例,我们打开主题压缩包:

\n

\"img\"

\n

你能看到里面有一个“图标.url”,双击打开它,即可在浏览器里面加载到图标下载页面,下载后,打开图标压缩包:

\n

\"img\"

\n

里面包含了“7tsp”和“iPack”文件夹,我们选择其中一种即可;再次说明,不论你选择的是哪一种,都必须先看下相应的教程再安装:iPack图标包安装注意事项

\n

修改桌面软件图标

软件图标就是前面提到的像QQ、微信之类的啦:

\n

\"img\"

\n

桌面上的图标分为两类:系统工具图标,如:此电脑、网络、文档、回收站;软件快捷方式图标,如:QQ、微信、360浏览器等…详细方法请参考:桌面图标更改方法

\n

修改任务栏软件图标

\"img\"

\n

任务栏软件图标,就是被你手动固定在任务栏上的那些软件的图标,如上图,详细方法请参考:任务栏软件图标修改方法

\n

\"img\"

\n

\"img\"

\n"},{"date":"2021-09-09T08:41:28.000Z","title":"MDK keil卸载芯片包packs","description":"卸载mdk keil5已经安装过的一些packs","cover":"https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png","abbrlink":"a4c109bd","_content":"\n\n\n\n## MDK Keil卸载packs\n\n比如我要卸载包:STM32F4XXX\n\n1. 打开MDK keil5软件,点击图中箭头所指的选项\n ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201121085508843.png#pic_center)\n\n2. 在左边选择devices,搜索 “stm32”,然后选择\n\n ![image-20210909163924334](https://gitee.com/ajream/images/raw/master/img/20210909163932_image-20210909163924334.png)\n\n3. 重启mdk keil5即可\n\n","source":"_posts/stm32/MDK_Keil卸载已经安装的pack.md","raw":"---\ndate: '2021-09-09 16:41:28'\ntitle: MDK keil卸载芯片包packs\ntags:\n - MDK-keil5\ncategories:\n - 硬件学习\n - stm32\ndescription: 卸载mdk keil5已经安装过的一些packs\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png'\nabbrlink: a4c109bd\n---\n\n\n\n\n## MDK Keil卸载packs\n\n比如我要卸载包:STM32F4XXX\n\n1. 打开MDK keil5软件,点击图中箭头所指的选项\n ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201121085508843.png#pic_center)\n\n2. 在左边选择devices,搜索 “stm32”,然后选择\n\n ![image-20210909163924334](https://gitee.com/ajream/images/raw/master/img/20210909163932_image-20210909163924334.png)\n\n3. 重启mdk keil5即可\n\n","slug":"stm32/MDK_Keil卸载已经安装的pack","published":1,"updated":"2021-09-10T12:48:27.623Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r9007sakvea9jb34oj","content":"

MDK Keil卸载packs

比如我要卸载包:STM32F4XXX

\n
    \n
  1. 打开MDK keil5软件,点击图中箭头所指的选项
    \"在这里插入图片描述\"

    \n
  2. \n
  3. 在左边选择devices,搜索 “stm32”,然后选择

    \n

    \"image-20210909163924334\"

    \n
  4. \n
  5. 重启mdk keil5即可

    \n
  6. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

MDK Keil卸载packs

比如我要卸载包:STM32F4XXX

\n
    \n
  1. 打开MDK keil5软件,点击图中箭头所指的选项
    \"在这里插入图片描述\"

    \n
  2. \n
  3. 在左边选择devices,搜索 “stm32”,然后选择

    \n

    \"image-20210909163924334\"

    \n
  4. \n
  5. 重启mdk keil5即可

    \n
  6. \n
\n"},{"title":"win10创建系统还原及如何还原","description":"Win10创建系统还原点及还原演示","cover":"https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png","abbrlink":"446faab7","swiper_index":1,"date":"2021-09-05T02:20:22.000Z","_content":"\n\n\n[原文查看](https://zhutix.com/10tutorials/huanyuan/)\n\n## Win10创建系统还原点及还原演示\n\n### 开启系统还原功能\n\n在此电脑右键 – 属性\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-01.png)\n\n在系统属性面板左侧选择系统保护(如下图)\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-02.png)\n\n选择C盘,点击配置(如下图)\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-03.png)\n\n启用系统保护,选择一个使用量(随意)如下图\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-04.png)\n\n### 创建系统还原点\n\n选择C盘,点击创建,输入一个名字,创建即可,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-05.png)\n\n稍等片刻即可创建成功,如下图\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-06.png)\n\n### 正常情况下还原系统\n\n如果你在使用电脑的途中想还原,可以再次打开系统保护界面,点击系统还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-07.png)\n\n选择一个要还原的时间,下一步,即可开始还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-08.png)\n\n### 无法进入系统情况下还原系统\n\n系统出现问题,无法进入的情况下我们可以进入WinRE还原系统:\n\n开机按F8进入WinRE(有的品牌电脑是按F11键或其他按键,请自行尝试或查询)或待系统启动到出现Windows徽标或品牌Logo时,长按电源键强制关机,如此强制关机三次后,系统因无法正常启动就会进入“恢复”界面或“自动修复”界面,点击“查看高级修复选项”或“高级选项”即可进入WinRE。如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-09.png)\n\n进入WinRE后我们进入疑难解答,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-10.png)\n\n高级选项:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-11.png)\n\n进入系统还原:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-12.png)\n\n选择一个要还原的时间,下一步,即可开始还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-13.png)\n\n\n\n### 视频演示\n\nhttp://v.youku.com/v_show/id_XNDM5MjYxMTI2NA==.html","source":"_posts/win10/win10创建系统还原节点.md","raw":"---\ntitle: win10创建系统还原及如何还原\ntags:\n - win10\ncategories: win10\ndescription: Win10创建系统还原点及还原演示\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png'\nabbrlink: 446faab7\nswiper_index: 1\ndate: 2021-09-05 10:20:22\n---\n\n\n\n[原文查看](https://zhutix.com/10tutorials/huanyuan/)\n\n## Win10创建系统还原点及还原演示\n\n### 开启系统还原功能\n\n在此电脑右键 – 属性\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-01.png)\n\n在系统属性面板左侧选择系统保护(如下图)\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-02.png)\n\n选择C盘,点击配置(如下图)\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-03.png)\n\n启用系统保护,选择一个使用量(随意)如下图\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-04.png)\n\n### 创建系统还原点\n\n选择C盘,点击创建,输入一个名字,创建即可,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-05.png)\n\n稍等片刻即可创建成功,如下图\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-06.png)\n\n### 正常情况下还原系统\n\n如果你在使用电脑的途中想还原,可以再次打开系统保护界面,点击系统还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-07.png)\n\n选择一个要还原的时间,下一步,即可开始还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-08.png)\n\n### 无法进入系统情况下还原系统\n\n系统出现问题,无法进入的情况下我们可以进入WinRE还原系统:\n\n开机按F8进入WinRE(有的品牌电脑是按F11键或其他按键,请自行尝试或查询)或待系统启动到出现Windows徽标或品牌Logo时,长按电源键强制关机,如此强制关机三次后,系统因无法正常启动就会进入“恢复”界面或“自动修复”界面,点击“查看高级修复选项”或“高级选项”即可进入WinRE。如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-09.png)\n\n进入WinRE后我们进入疑难解答,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-10.png)\n\n高级选项:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-11.png)\n\n进入系统还原:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-12.png)\n\n选择一个要还原的时间,下一步,即可开始还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-13.png)\n\n\n\n### 视频演示\n\nhttp://v.youku.com/v_show/id_XNDM5MjYxMTI2NA==.html","slug":"win10/win10创建系统还原节点","published":1,"updated":"2021-09-08T14:45:51.921Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rb007wakve7c6t6uot","content":"

原文查看

\n

Win10创建系统还原点及还原演示

开启系统还原功能

在此电脑右键 – 属性

\n

\"img\"

\n

在系统属性面板左侧选择系统保护(如下图)

\n

\"img\"

\n

选择C盘,点击配置(如下图)

\n

\"img\"

\n

启用系统保护,选择一个使用量(随意)如下图

\n

\"img\"

\n

创建系统还原点

选择C盘,点击创建,输入一个名字,创建即可,如下图:

\n

\"img\"

\n

稍等片刻即可创建成功,如下图

\n

\"img\"

\n

正常情况下还原系统

如果你在使用电脑的途中想还原,可以再次打开系统保护界面,点击系统还原,如下图:

\n

\"img\"

\n

选择一个要还原的时间,下一步,即可开始还原,如下图:

\n

\"img\"

\n

无法进入系统情况下还原系统

系统出现问题,无法进入的情况下我们可以进入WinRE还原系统:

\n

开机按F8进入WinRE(有的品牌电脑是按F11键或其他按键,请自行尝试或查询)或待系统启动到出现Windows徽标或品牌Logo时,长按电源键强制关机,如此强制关机三次后,系统因无法正常启动就会进入“恢复”界面或“自动修复”界面,点击“查看高级修复选项”或“高级选项”即可进入WinRE。如下图:

\n

\"img\"

\n

进入WinRE后我们进入疑难解答,如下图:

\n

\"img\"

\n

高级选项:

\n

\"img\"

\n

进入系统还原:

\n

\"img\"

\n

选择一个要还原的时间,下一步,即可开始还原,如下图:

\n

\"img\"

\n

视频演示

http://v.youku.com/v_show/id_XNDM5MjYxMTI2NA==.html

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

原文查看

\n

Win10创建系统还原点及还原演示

开启系统还原功能

在此电脑右键 – 属性

\n

\"img\"

\n

在系统属性面板左侧选择系统保护(如下图)

\n

\"img\"

\n

选择C盘,点击配置(如下图)

\n

\"img\"

\n

启用系统保护,选择一个使用量(随意)如下图

\n

\"img\"

\n

创建系统还原点

选择C盘,点击创建,输入一个名字,创建即可,如下图:

\n

\"img\"

\n

稍等片刻即可创建成功,如下图

\n

\"img\"

\n

正常情况下还原系统

如果你在使用电脑的途中想还原,可以再次打开系统保护界面,点击系统还原,如下图:

\n

\"img\"

\n

选择一个要还原的时间,下一步,即可开始还原,如下图:

\n

\"img\"

\n

无法进入系统情况下还原系统

系统出现问题,无法进入的情况下我们可以进入WinRE还原系统:

\n

开机按F8进入WinRE(有的品牌电脑是按F11键或其他按键,请自行尝试或查询)或待系统启动到出现Windows徽标或品牌Logo时,长按电源键强制关机,如此强制关机三次后,系统因无法正常启动就会进入“恢复”界面或“自动修复”界面,点击“查看高级修复选项”或“高级选项”即可进入WinRE。如下图:

\n

\"img\"

\n

进入WinRE后我们进入疑难解答,如下图:

\n

\"img\"

\n

高级选项:

\n

\"img\"

\n

进入系统还原:

\n

\"img\"

\n

选择一个要还原的时间,下一步,即可开始还原,如下图:

\n

\"img\"

\n

视频演示

http://v.youku.com/v_show/id_XNDM5MjYxMTI2NA==.html

\n"},{"title":"stm32标准外设库介绍","date":"2021-09-11T06:00:25.000Z","description":"认识stm32标准外设库,目录结构、文件作用","cover":"https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png","abbrlink":"1294a4bd","_content":"\n\n\n\n\n\n\n\n## 固件库介绍\n\n\nSTM32 标准函数库,由 ST 公司针对 STM32 提供的函数接口,即API(Application Program Interface),开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。\n\n\n\n\n\n\n\n\n\n## 下载\n\n建议去[官网](https://www.stmicroelectronics.com.cn/zh/embedded-software/stm32-standard-peripheral-libraries.html?querycriteria=productId=LN1939)搜索下载最新版\n\n![image-20210911141115314](https://gitee.com/ajream/images/raw/master/img/20210911141118_image-20210911141115314.png)\n\n\n\n下载(第一次下载需要输入邮箱,然后会发来一封邮件,点击邮件的链接进行下载)\n\n![image-20210911141332276](https://gitee.com/ajream/images/raw/master/img/20210911141333_image-20210911141332276.png)\n\n\n\n下载完成随便找个位置进行解压即可,后面建立工程时要使用的话再把其中的文件复制到我们自己的工程即可\n\n\n\n## 体系架构\n\n这是解压后固件库的目录结构\n\n| ![image-20210911141948049](https://gitee.com/ajream/images/raw/master/img/20210911141949_image-20210911141948049.png) | ![](https://img-blog.csdnimg.cn/20190302162843226.png) |\n| ------------------------------------------------------------ | ------------------------------------------------------ |\n\n\n\n\n\n\n\n体系架构:\n\n![image-20210911151837136](https://gitee.com/ajream/images/raw/master/img/20210911151840_image-20210911151837136.png)\n\n\n\n1. 用户层\n\n 用户层位于架构的最顶端,包含与用户编程有关的所有文件,`main.c`, `stm32f10x_it.c`, `stm32f10x_it.h`, `stm32f10x_conf.h`, 用户使用固件库进行开发,主要编写`main.c`和`stm32f10x_it.c`\n\n2. CMSIS层\n\n CMSIS层位于体系架构的中间,向下负责与内核和各个外设打交道,向上提供函数接口供用户程序或操作系统调用。CMSIS层主要由设备外设函数和CMSIS核心层构成\n\n ![image-20210911153147123](https://gitee.com/ajream/images/raw/master/img/20210911153148_image-20210911153147123.png)\n\n \n\n - 设备外设函数:由各个芯片产商提供\n\n - misc.c/misc.h:NVIC代码\n \n - stm32f10x_ppp.c/stm32f10x_ppp.h:外设驱动代码\n \n \n \n - CMSIS核心层:包括核内外设访问层(arm公司提供)和设备外设访问层(ST公司提供)\n \n - core_cm3.c/core_cm3.h:Cortex-M3内核通用源文件、头文件\n \n - stm32f10x.h/stm32f10x.c/system_stm32f10x.h等:包含了核外外设寄存器名称、地址、中断向量定义等\n\n\n \"image-20210911161452989\"\n\n3. 微控制器(MCU)层\n\n 微控制器层也叫硬件层,位于架构体系最底层\n\n\n\n","source":"_posts/stm32/stm32标准外设库介绍.md","raw":"---\ntitle: stm32标准外设库介绍\ndate: '2021-09-11 14:00:25'\ntags:\n - stm32\ncategories:\n - 硬件学习\n - stm32\ndescription: 认识stm32标准外设库,目录结构、文件作用\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png'\nabbrlink: 1294a4bd\n---\n\n\n\n\n\n\n\n\n## 固件库介绍\n\n\nSTM32 标准函数库,由 ST 公司针对 STM32 提供的函数接口,即API(Application Program Interface),开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。\n\n\n\n\n\n\n\n\n\n## 下载\n\n建议去[官网](https://www.stmicroelectronics.com.cn/zh/embedded-software/stm32-standard-peripheral-libraries.html?querycriteria=productId=LN1939)搜索下载最新版\n\n![image-20210911141115314](https://gitee.com/ajream/images/raw/master/img/20210911141118_image-20210911141115314.png)\n\n\n\n下载(第一次下载需要输入邮箱,然后会发来一封邮件,点击邮件的链接进行下载)\n\n![image-20210911141332276](https://gitee.com/ajream/images/raw/master/img/20210911141333_image-20210911141332276.png)\n\n\n\n下载完成随便找个位置进行解压即可,后面建立工程时要使用的话再把其中的文件复制到我们自己的工程即可\n\n\n\n## 体系架构\n\n这是解压后固件库的目录结构\n\n| ![image-20210911141948049](https://gitee.com/ajream/images/raw/master/img/20210911141949_image-20210911141948049.png) | ![](https://img-blog.csdnimg.cn/20190302162843226.png) |\n| ------------------------------------------------------------ | ------------------------------------------------------ |\n\n\n\n\n\n\n\n体系架构:\n\n![image-20210911151837136](https://gitee.com/ajream/images/raw/master/img/20210911151840_image-20210911151837136.png)\n\n\n\n1. 用户层\n\n 用户层位于架构的最顶端,包含与用户编程有关的所有文件,`main.c`, `stm32f10x_it.c`, `stm32f10x_it.h`, `stm32f10x_conf.h`, 用户使用固件库进行开发,主要编写`main.c`和`stm32f10x_it.c`\n\n2. CMSIS层\n\n CMSIS层位于体系架构的中间,向下负责与内核和各个外设打交道,向上提供函数接口供用户程序或操作系统调用。CMSIS层主要由设备外设函数和CMSIS核心层构成\n\n ![image-20210911153147123](https://gitee.com/ajream/images/raw/master/img/20210911153148_image-20210911153147123.png)\n\n \n\n - 设备外设函数:由各个芯片产商提供\n\n - misc.c/misc.h:NVIC代码\n \n - stm32f10x_ppp.c/stm32f10x_ppp.h:外设驱动代码\n \n \n \n - CMSIS核心层:包括核内外设访问层(arm公司提供)和设备外设访问层(ST公司提供)\n \n - core_cm3.c/core_cm3.h:Cortex-M3内核通用源文件、头文件\n \n - stm32f10x.h/stm32f10x.c/system_stm32f10x.h等:包含了核外外设寄存器名称、地址、中断向量定义等\n\n\n \"image-20210911161452989\"\n\n3. 微控制器(MCU)层\n\n 微控制器层也叫硬件层,位于架构体系最底层\n\n\n\n","slug":"stm32/stm32标准外设库介绍","published":1,"updated":"2021-09-12T03:38:57.863Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rc007yakve8853hjrp","content":"

固件库介绍

STM32 标准函数库,由 ST 公司针对 STM32 提供的函数接口,即API(Application Program Interface),开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。

\n

下载

建议去官网搜索下载最新版

\n

\"image-20210911141115314\"

\n

下载(第一次下载需要输入邮箱,然后会发来一封邮件,点击邮件的链接进行下载)

\n

\"image-20210911141332276\"

\n

下载完成随便找个位置进行解压即可,后面建立工程时要使用的话再把其中的文件复制到我们自己的工程即可

\n

体系架构

这是解压后固件库的目录结构

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n
\"image-20210911141948049\"\"\"
\n
\n

体系架构:

\n

\"image-20210911151837136\"

\n
    \n
  1. 用户层

    \n

    用户层位于架构的最顶端,包含与用户编程有关的所有文件,main.c, stm32f10x_it.c, stm32f10x_it.h, stm32f10x_conf.h, 用户使用固件库进行开发,主要编写main.cstm32f10x_it.c

    \n
  2. \n
  3. CMSIS层

    \n

    CMSIS层位于体系架构的中间,向下负责与内核和各个外设打交道,向上提供函数接口供用户程序或操作系统调用。CMSIS层主要由设备外设函数和CMSIS核心层构成

    \n

    \"image-20210911153147123\"

    \n
  4. \n
\n
    \n
  • 设备外设函数:由各个芯片产商提供

    \n
      \n
    • misc.c/misc.h:NVIC代码

      \n
    • \n
    • stm32f10x_ppp.c/stm32f10x_ppp.h:外设驱动代码

      \n
    • \n
    \n
  • \n
\n
    \n
  • CMSIS核心层:包括核内外设访问层(arm公司提供)和设备外设访问层(ST公司提供)

    \n
      \n
    • core_cm3.c/core_cm3.h:Cortex-M3内核通用源文件、头文件

      \n
    • \n
    • stm32f10x.h/stm32f10x.c/system_stm32f10x.h等:包含了核外外设寄存器名称、地址、中断向量定义等

      \n
    • \n
    \n
  • \n
\n

\"image-20210911161452989\"

\n
    \n
  1. 微控制器(MCU)层

    \n

    微控制器层也叫硬件层,位于架构体系最底层

    \n
  2. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

固件库介绍

STM32 标准函数库,由 ST 公司针对 STM32 提供的函数接口,即API(Application Program Interface),开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。

\n

下载

建议去官网搜索下载最新版

\n

\"image-20210911141115314\"

\n

下载(第一次下载需要输入邮箱,然后会发来一封邮件,点击邮件的链接进行下载)

\n

\"image-20210911141332276\"

\n

下载完成随便找个位置进行解压即可,后面建立工程时要使用的话再把其中的文件复制到我们自己的工程即可

\n

体系架构

这是解压后固件库的目录结构

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n
\"image-20210911141948049\"\"\"
\n
\n

体系架构:

\n

\"image-20210911151837136\"

\n
    \n
  1. 用户层

    \n

    用户层位于架构的最顶端,包含与用户编程有关的所有文件,main.c, stm32f10x_it.c, stm32f10x_it.h, stm32f10x_conf.h, 用户使用固件库进行开发,主要编写main.cstm32f10x_it.c

    \n
  2. \n
  3. CMSIS层

    \n

    CMSIS层位于体系架构的中间,向下负责与内核和各个外设打交道,向上提供函数接口供用户程序或操作系统调用。CMSIS层主要由设备外设函数和CMSIS核心层构成

    \n

    \"image-20210911153147123\"

    \n
  4. \n
\n
    \n
  • 设备外设函数:由各个芯片产商提供

    \n
      \n
    • misc.c/misc.h:NVIC代码

      \n
    • \n
    • stm32f10x_ppp.c/stm32f10x_ppp.h:外设驱动代码

      \n
    • \n
    \n
  • \n
\n
    \n
  • CMSIS核心层:包括核内外设访问层(arm公司提供)和设备外设访问层(ST公司提供)

    \n
      \n
    • core_cm3.c/core_cm3.h:Cortex-M3内核通用源文件、头文件

      \n
    • \n
    • stm32f10x.h/stm32f10x.c/system_stm32f10x.h等:包含了核外外设寄存器名称、地址、中断向量定义等

      \n
    • \n
    \n
  • \n
\n

\"image-20210911161452989\"

\n
    \n
  1. 微控制器(MCU)层

    \n

    微控制器层也叫硬件层,位于架构体系最底层

    \n
  2. \n
\n"},{"date":"2021-09-10T08:41:28.000Z","title":"MDK keil新建stm32工程","description":"一步步带你使用mdk keil建立第一个stm32关于库函数(不是寄存器)的project","cover":"https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png","abbrlink":"190b30be","_content":"\n\n\n\n\n\n\n\n\n## 工程结构建立\n\n\n\n### 初始化工程目录结构\n\n首先创建一个文件夹test, 用于存放该工程,然后在该文件夹下创建以下几个文件夹(目的是让工程管理起来更有条理):\n\n- lib: 用于存放库文件(像Libraries、Lib等文件名字也是可以的,根据自己的风格,最好是能\"见名知义\")\n - CMSIS: 存放内核核心库文件\n - STM32F10x_StdPeriph_Driver: 是STM32Fl0x标准外设驱动库函数目录,存放STM32F10x微控制器的外设驱动(这个文件夹可以不建立,等下直接把库的拷贝过来)\n- obj: 用于存放程序编译输出的各种文件\n- user: 用于存放main.c等用户自己的文件\n\n\n\n### 添加相关文件\n\n#### 固件库说明\n\n{%folding blue,展开%}\n\n首先要去官网或其他渠道下载STM32固件库,这个库包含了我们创建要用到的所有文件,其包含的文件如下\n\n![image-20210910194646565](https://gitee.com/ajream/images/raw/master/img/20210910194652_image-20210910194646565.png)\n\n\n\n【Libraries】文件夹存放STM32F10x开发要用到的各种库函数和启动文件,包括`CMSIS`和`STM32F10x_StdPeriph_Driver`两个子文件夹:\n\n1. 【CMSIS】文件夹是内核库核心文件夹,包括CoreSupport和DeviceSupport等两个文件夹 \n\n CoreSupport文件夹包括Cortcx-M3内核通用源文件`core_cm3.c`和头文件`core_cm3.h`\n \n2. 【DeviceSupport】文件夹STM32F0x头文件`stm32f10x.h`和系统初始化文件`system_stm32f10x.c`【STM32F10x_StdPeriph_Driver】文件夹是STM32Fl0x标准外设驱动库函数目录,包括了所有STM32F10x微控制器的外设驱动\n\n![image-20210910195021702](https://gitee.com/ajream/images/raw/master/img/20210910195026_image-20210910195021702.png)\n\n\n\n【Project】文件夹对应STM32F10x标准外设库体系架构中的用户层,用来存放ST官方提供的STM32F10x工程模板和外设驱动示例\n\n【Utilities】文件夹用于存放ST官方评估板的BSP(Board Support Package, 板级支持包)和额外的第三方固件\n\n\n\n{%endfolding%}\n\n\n\n#### 添加库文件到自己的模板\n\n##### lib目录-1\n\n在库文件找到下面的文件拷贝到自己刚建立的`lib/CMSIS`目录\n\n添加完成后 `lib/CMSIS`目录是这样:\n\n![image-20210910201435838](https://gitee.com/ajream/images/raw/master/img/20210910201440_image-20210910201435838.png)\n\n这些文件去哪里找:在库文件的 【Libraries】目录下\n\n内核通用源文件\n\n![image-20210910200517197](https://gitee.com/ajream/images/raw/master/img/20210910200521_image-20210910200517197.png)\n\n启动文件,只需要红框圈起来的那个,对应 stm32f103ZE开发板芯片\n\n![image-20210910200757380](https://gitee.com/ajream/images/raw/master/img/20210910200802_image-20210910200757380.png)\n\n系统初始化文件\n\n![image-20210910201253535](https://gitee.com/ajream/images/raw/master/img/20210910201256_image-20210910201253535.png)\n\n##### `lib`目录-2\n\n去固件库的【Libraries】文件夹下【如下图】把文件夹`STM32F10x_StdPeriph_Driver`拷贝到自己建立的 `lib` 目录下\n\n![image-20210910201957385](https://gitee.com/ajream/images/raw/master/img/20210910202001_image-20210910201957385.png)\n\n##### `user`目录\n\n这是添加完成后的`user`目录:\n\n![image-20210910202442964](https://gitee.com/ajream/images/raw/master/img/20210910202446_image-20210910202442964.png)\n\n这些文件在哪里找:\n\n- main.c: 可以自己新建一个空的`main.c`文件,也可以去官方模板那里拷贝过来(官方模板在 `/Project/STM32F10x_StdPeriph_Template/`目录下, 下面第1幅图)\n- 最下面的三个`stm32f10x_conf.h`、`stm32f10x_it.c`、`stm32f10x_it.h`: 去官方模板拷贝\n- 第二个stm32f10x.h: 在库的`Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x`路径下找(下面第2幅图)\n\n\n\n![image-20210910202705273](https://gitee.com/ajream/images/raw/master/img/20210910202708_image-20210910202705273.png)\n\n\n\n![image-20210910203037259](https://gitee.com/ajream/images/raw/master/img/20210910203042_image-20210910203037259.png)\n\n\n\n{%note info flat%}\n\n注意,以上步骤只是在磁盘上建立了这么一个目录结构,keil还不知道有这个工程,所以我们要在keil新建一个工程,然后把这个工程与存放在磁盘上的目录(还不能叫工程)进行关联,这样才能形成一个完整结构的工程。\n\n{%endnote%}\n\n### 打开mdk keil新建stm32工程\n\n这个步骤是为了在keil中新建一个工程与存放在磁盘上的目录(还不能叫工程)进行关联映射,所以有2步:1-新建工程, 2-进行关联\n\n#### 新建工程\n\n注意新建工程时,有几个步骤:\n\n1. 工程名,任意(根据自己的命名风格,不能有中文)\n2. 选择芯片,这里使用 `STM32F103ZE`\n3. 选择库,这里先不选,后面再选,直接跳过\n\n{%folding blue, 具体步骤%}\n\n点击 `new uVision Project`\n\n![image-20210910210845401](https://gitee.com/ajream/images/raw/master/img/20210910210853_image-20210910210845401.png)\n\n自己命名,选择保存;\n\n选择芯片 `STM32F103ZE`\n\n![image-20210910211314033](https://gitee.com/ajream/images/raw/master/img/20210910211318_image-20210910211314033.png)\n\n\n\n选择库,直接跳过,后面再选\n\n![image-20210910211418672](https://gitee.com/ajream/images/raw/master/img/20210910211421_image-20210910211418672.png)\n\n完成\n\n![image-20210910211508362](https://gitee.com/ajream/images/raw/master/img/20210910211512_image-20210910211508362.png)\n\n{%endfolding%}\n\n\n\n#### 完善工程结构\n\n这里要把这个工程与磁盘上的目录和文件进行映射关联\n\n选择工程管理,如下:\n\n![image-20210910212307092](https://gitee.com/ajream/images/raw/master/img/20210910212322_image-20210910212307092.png)\n\n\n\n依次新建这几个目录:\n\n- user: 存放用户代码文件\n- startup: 存放启动文件\n- CMSIS: 内核库文件\n- StdPeriph_Driver: 外设库文件\n\n![image-20210910212703182](https://gitee.com/ajream/images/raw/master/img/20210910212706_image-20210910212703182.png)\n\n\n\n分别往这几个目录添加文件:\n\n![image-20210910214210061](https://gitee.com/ajream/images/raw/master/img/20210910214215_image-20210910214210061.png)\n\n\n\nuser(从 `/user/`目录下选择)\n\n- main.c\n- stm32f10x_it.c\n\nstartup(从 `/lib/CMSIS/` 目录下选择)\n\n- startup_stm32f10x_hd.s(注意添加时没显示出来要选择 文件类型为 \"`All files(*.*)`\")\n\nCMSIS(从 `/lib/CMSIS/`目录下选择)\n\n- core_cm3.c\n- system_stm32f10x.c\n\nStdPeriph_Driver(从`/lib/STM32F10x_StdPeriph_Driver/src/`目录下选择,可以选择全部,也可以只选择下面两个,根据自己需要,下面这2个一般都会用到,所以都添加进来)\n\n- stm32f10x_rcc.c\n- stm32f10x_gpio.c\n\n\n\n添加完成如下:\n\n![image-20210910214412880](https://gitee.com/ajream/images/raw/master/img/20210910214416_image-20210910214412880.png)\n\n\n\n{%note info flat%}\n\n到此工程结构已经建立完成,可以在main.c文件下写代码了,但是现在还不能编译,因为编译需要的头文件还没包含进来,以及编译时的其它一些配置还没完成\n\n\n\n{%endnote%}\n\n\n\n\n\n## 编译配置\n\n选择这个魔法棒\n\n![image-20210910215321021](https://gitee.com/ajream/images/raw/master/img/20210910215324_image-20210910215321021.png)\n\n\n\n配置Target项\n\n![image-20210910215245013](https://gitee.com/ajream/images/raw/master/img/20210910215248_image-20210910215245013.png)\n\n\n\n配置output项\n\n![image-20210910220000653](https://gitee.com/ajream/images/raw/master/img/20210910220004_image-20210910220000653.png)\n\n\n\n\n\n配置 listing项\n\n![image-20210910220055189](https://gitee.com/ajream/images/raw/master/img/20210910220059_image-20210910220055189.png)\n\n\n\n配置 C/C++ 项,目的是为了把头文件添加进来\n\n下图的第2步添加的是:`USE_STDPERIPH_DRIVER,STM32F10X_HD`(注意中间有个英文逗号),表示使用外部库函数驱动\n\n![image-20210910220625228](https://gitee.com/ajream/images/raw/master/img/20210910220629_image-20210910220625228.png)\n\n\n\n\n\n配置Debug项:ARM仿真器选项配置(手中有开发板可以先通过ARM仿真器连接电脑)\n\n![image-20210910221520551](https://gitee.com/ajream/images/raw/master/img/20210910221523_image-20210910221520551.png)\n\n\n\n下载配置(在上一步的Debugger配置中:Debugger->Settings->FlashDownload)\n\n![image-20210910221912505](https://gitee.com/ajream/images/raw/master/img/20210910221917_image-20210910221912505.png)\n\n\n\n\n\n{%note info flat%}\n\n到此所有配置完成,选择ok,可以愉快的写代码了\n\n\n\n{%endnote%}\n\n\n\n\n\n## 测试\n\n打开main.c文件,删除原有代码(如果有的话),添加如下代码:\n\n\n\n```c\n#include \"stm32f10x.h\"\n\nint main(){\n\t\n\twhile(1){\n \n\t}\n}\n\n```\n\n\n\n编译运行,没有报错,完美。。。。\n\n![image-20210910222416127](https://gitee.com/ajream/images/raw/master/img/20210910222420_image-20210910222416127.png)\n\n\n\n\n\n","source":"_posts/stm32/keil新建库函数版本的project.md","raw":"---\ndate: '2021-09-10 16:41:28'\ntitle: MDK keil新建stm32工程\ntags:\n - stm32\ncategories:\n - 硬件学习\n - stm32\ndescription: 一步步带你使用mdk keil建立第一个stm32关于库函数(不是寄存器)的project\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png'\nabbrlink: 190b30be\n---\n\n\n\n\n\n\n\n\n\n## 工程结构建立\n\n\n\n### 初始化工程目录结构\n\n首先创建一个文件夹test, 用于存放该工程,然后在该文件夹下创建以下几个文件夹(目的是让工程管理起来更有条理):\n\n- lib: 用于存放库文件(像Libraries、Lib等文件名字也是可以的,根据自己的风格,最好是能\"见名知义\")\n - CMSIS: 存放内核核心库文件\n - STM32F10x_StdPeriph_Driver: 是STM32Fl0x标准外设驱动库函数目录,存放STM32F10x微控制器的外设驱动(这个文件夹可以不建立,等下直接把库的拷贝过来)\n- obj: 用于存放程序编译输出的各种文件\n- user: 用于存放main.c等用户自己的文件\n\n\n\n### 添加相关文件\n\n#### 固件库说明\n\n{%folding blue,展开%}\n\n首先要去官网或其他渠道下载STM32固件库,这个库包含了我们创建要用到的所有文件,其包含的文件如下\n\n![image-20210910194646565](https://gitee.com/ajream/images/raw/master/img/20210910194652_image-20210910194646565.png)\n\n\n\n【Libraries】文件夹存放STM32F10x开发要用到的各种库函数和启动文件,包括`CMSIS`和`STM32F10x_StdPeriph_Driver`两个子文件夹:\n\n1. 【CMSIS】文件夹是内核库核心文件夹,包括CoreSupport和DeviceSupport等两个文件夹 \n\n CoreSupport文件夹包括Cortcx-M3内核通用源文件`core_cm3.c`和头文件`core_cm3.h`\n \n2. 【DeviceSupport】文件夹STM32F0x头文件`stm32f10x.h`和系统初始化文件`system_stm32f10x.c`【STM32F10x_StdPeriph_Driver】文件夹是STM32Fl0x标准外设驱动库函数目录,包括了所有STM32F10x微控制器的外设驱动\n\n![image-20210910195021702](https://gitee.com/ajream/images/raw/master/img/20210910195026_image-20210910195021702.png)\n\n\n\n【Project】文件夹对应STM32F10x标准外设库体系架构中的用户层,用来存放ST官方提供的STM32F10x工程模板和外设驱动示例\n\n【Utilities】文件夹用于存放ST官方评估板的BSP(Board Support Package, 板级支持包)和额外的第三方固件\n\n\n\n{%endfolding%}\n\n\n\n#### 添加库文件到自己的模板\n\n##### lib目录-1\n\n在库文件找到下面的文件拷贝到自己刚建立的`lib/CMSIS`目录\n\n添加完成后 `lib/CMSIS`目录是这样:\n\n![image-20210910201435838](https://gitee.com/ajream/images/raw/master/img/20210910201440_image-20210910201435838.png)\n\n这些文件去哪里找:在库文件的 【Libraries】目录下\n\n内核通用源文件\n\n![image-20210910200517197](https://gitee.com/ajream/images/raw/master/img/20210910200521_image-20210910200517197.png)\n\n启动文件,只需要红框圈起来的那个,对应 stm32f103ZE开发板芯片\n\n![image-20210910200757380](https://gitee.com/ajream/images/raw/master/img/20210910200802_image-20210910200757380.png)\n\n系统初始化文件\n\n![image-20210910201253535](https://gitee.com/ajream/images/raw/master/img/20210910201256_image-20210910201253535.png)\n\n##### `lib`目录-2\n\n去固件库的【Libraries】文件夹下【如下图】把文件夹`STM32F10x_StdPeriph_Driver`拷贝到自己建立的 `lib` 目录下\n\n![image-20210910201957385](https://gitee.com/ajream/images/raw/master/img/20210910202001_image-20210910201957385.png)\n\n##### `user`目录\n\n这是添加完成后的`user`目录:\n\n![image-20210910202442964](https://gitee.com/ajream/images/raw/master/img/20210910202446_image-20210910202442964.png)\n\n这些文件在哪里找:\n\n- main.c: 可以自己新建一个空的`main.c`文件,也可以去官方模板那里拷贝过来(官方模板在 `/Project/STM32F10x_StdPeriph_Template/`目录下, 下面第1幅图)\n- 最下面的三个`stm32f10x_conf.h`、`stm32f10x_it.c`、`stm32f10x_it.h`: 去官方模板拷贝\n- 第二个stm32f10x.h: 在库的`Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x`路径下找(下面第2幅图)\n\n\n\n![image-20210910202705273](https://gitee.com/ajream/images/raw/master/img/20210910202708_image-20210910202705273.png)\n\n\n\n![image-20210910203037259](https://gitee.com/ajream/images/raw/master/img/20210910203042_image-20210910203037259.png)\n\n\n\n{%note info flat%}\n\n注意,以上步骤只是在磁盘上建立了这么一个目录结构,keil还不知道有这个工程,所以我们要在keil新建一个工程,然后把这个工程与存放在磁盘上的目录(还不能叫工程)进行关联,这样才能形成一个完整结构的工程。\n\n{%endnote%}\n\n### 打开mdk keil新建stm32工程\n\n这个步骤是为了在keil中新建一个工程与存放在磁盘上的目录(还不能叫工程)进行关联映射,所以有2步:1-新建工程, 2-进行关联\n\n#### 新建工程\n\n注意新建工程时,有几个步骤:\n\n1. 工程名,任意(根据自己的命名风格,不能有中文)\n2. 选择芯片,这里使用 `STM32F103ZE`\n3. 选择库,这里先不选,后面再选,直接跳过\n\n{%folding blue, 具体步骤%}\n\n点击 `new uVision Project`\n\n![image-20210910210845401](https://gitee.com/ajream/images/raw/master/img/20210910210853_image-20210910210845401.png)\n\n自己命名,选择保存;\n\n选择芯片 `STM32F103ZE`\n\n![image-20210910211314033](https://gitee.com/ajream/images/raw/master/img/20210910211318_image-20210910211314033.png)\n\n\n\n选择库,直接跳过,后面再选\n\n![image-20210910211418672](https://gitee.com/ajream/images/raw/master/img/20210910211421_image-20210910211418672.png)\n\n完成\n\n![image-20210910211508362](https://gitee.com/ajream/images/raw/master/img/20210910211512_image-20210910211508362.png)\n\n{%endfolding%}\n\n\n\n#### 完善工程结构\n\n这里要把这个工程与磁盘上的目录和文件进行映射关联\n\n选择工程管理,如下:\n\n![image-20210910212307092](https://gitee.com/ajream/images/raw/master/img/20210910212322_image-20210910212307092.png)\n\n\n\n依次新建这几个目录:\n\n- user: 存放用户代码文件\n- startup: 存放启动文件\n- CMSIS: 内核库文件\n- StdPeriph_Driver: 外设库文件\n\n![image-20210910212703182](https://gitee.com/ajream/images/raw/master/img/20210910212706_image-20210910212703182.png)\n\n\n\n分别往这几个目录添加文件:\n\n![image-20210910214210061](https://gitee.com/ajream/images/raw/master/img/20210910214215_image-20210910214210061.png)\n\n\n\nuser(从 `/user/`目录下选择)\n\n- main.c\n- stm32f10x_it.c\n\nstartup(从 `/lib/CMSIS/` 目录下选择)\n\n- startup_stm32f10x_hd.s(注意添加时没显示出来要选择 文件类型为 \"`All files(*.*)`\")\n\nCMSIS(从 `/lib/CMSIS/`目录下选择)\n\n- core_cm3.c\n- system_stm32f10x.c\n\nStdPeriph_Driver(从`/lib/STM32F10x_StdPeriph_Driver/src/`目录下选择,可以选择全部,也可以只选择下面两个,根据自己需要,下面这2个一般都会用到,所以都添加进来)\n\n- stm32f10x_rcc.c\n- stm32f10x_gpio.c\n\n\n\n添加完成如下:\n\n![image-20210910214412880](https://gitee.com/ajream/images/raw/master/img/20210910214416_image-20210910214412880.png)\n\n\n\n{%note info flat%}\n\n到此工程结构已经建立完成,可以在main.c文件下写代码了,但是现在还不能编译,因为编译需要的头文件还没包含进来,以及编译时的其它一些配置还没完成\n\n\n\n{%endnote%}\n\n\n\n\n\n## 编译配置\n\n选择这个魔法棒\n\n![image-20210910215321021](https://gitee.com/ajream/images/raw/master/img/20210910215324_image-20210910215321021.png)\n\n\n\n配置Target项\n\n![image-20210910215245013](https://gitee.com/ajream/images/raw/master/img/20210910215248_image-20210910215245013.png)\n\n\n\n配置output项\n\n![image-20210910220000653](https://gitee.com/ajream/images/raw/master/img/20210910220004_image-20210910220000653.png)\n\n\n\n\n\n配置 listing项\n\n![image-20210910220055189](https://gitee.com/ajream/images/raw/master/img/20210910220059_image-20210910220055189.png)\n\n\n\n配置 C/C++ 项,目的是为了把头文件添加进来\n\n下图的第2步添加的是:`USE_STDPERIPH_DRIVER,STM32F10X_HD`(注意中间有个英文逗号),表示使用外部库函数驱动\n\n![image-20210910220625228](https://gitee.com/ajream/images/raw/master/img/20210910220629_image-20210910220625228.png)\n\n\n\n\n\n配置Debug项:ARM仿真器选项配置(手中有开发板可以先通过ARM仿真器连接电脑)\n\n![image-20210910221520551](https://gitee.com/ajream/images/raw/master/img/20210910221523_image-20210910221520551.png)\n\n\n\n下载配置(在上一步的Debugger配置中:Debugger->Settings->FlashDownload)\n\n![image-20210910221912505](https://gitee.com/ajream/images/raw/master/img/20210910221917_image-20210910221912505.png)\n\n\n\n\n\n{%note info flat%}\n\n到此所有配置完成,选择ok,可以愉快的写代码了\n\n\n\n{%endnote%}\n\n\n\n\n\n## 测试\n\n打开main.c文件,删除原有代码(如果有的话),添加如下代码:\n\n\n\n```c\n#include \"stm32f10x.h\"\n\nint main(){\n\t\n\twhile(1){\n \n\t}\n}\n\n```\n\n\n\n编译运行,没有报错,完美。。。。\n\n![image-20210910222416127](https://gitee.com/ajream/images/raw/master/img/20210910222420_image-20210910222416127.png)\n\n\n\n\n\n","slug":"stm32/keil新建库函数版本的project","published":1,"updated":"2021-09-10T14:47:50.686Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rd0083akve82iv1jsm","content":"

工程结构建立

初始化工程目录结构

首先创建一个文件夹test, 用于存放该工程,然后在该文件夹下创建以下几个文件夹(目的是让工程管理起来更有条理):

\n
    \n
  • lib: 用于存放库文件(像Libraries、Lib等文件名字也是可以的,根据自己的风格,最好是能”见名知义”)
      \n
    • CMSIS: 存放内核核心库文件
    • \n
    • STM32F10x_StdPeriph_Driver: 是STM32Fl0x标准外设驱动库函数目录,存放STM32F10x微控制器的外设驱动(这个文件夹可以不建立,等下直接把库的拷贝过来)
    • \n
    \n
  • \n
  • obj: 用于存放程序编译输出的各种文件
  • \n
  • user: 用于存放main.c等用户自己的文件
  • \n
\n

添加相关文件

固件库说明

展开 \n
\n

首先要去官网或其他渠道下载STM32固件库,这个库包含了我们创建要用到的所有文件,其包含的文件如下

\"image-20210910194646565\"

【Libraries】文件夹存放STM32F10x开发要用到的各种库函数和启动文件,包括CMSISSTM32F10x_StdPeriph_Driver两个子文件夹:

  1. 【CMSIS】文件夹是内核库核心文件夹,包括CoreSupport和DeviceSupport等两个文件夹

    CoreSupport文件夹包括Cortcx-M3内核通用源文件core_cm3.c和头文件core_cm3.h

  2. 【DeviceSupport】文件夹STM32F0x头文件stm32f10x.h和系统初始化文件system_stm32f10x.c【STM32F10x_StdPeriph_Driver】文件夹是STM32Fl0x标准外设驱动库函数目录,包括了所有STM32F10x微控制器的外设驱动

\"image-20210910195021702\"

【Project】文件夹对应STM32F10x标准外设库体系架构中的用户层,用来存放ST官方提供的STM32F10x工程模板和外设驱动示例

【Utilities】文件夹用于存放ST官方评估板的BSP(Board Support Package, 板级支持包)和额外的第三方固件

\n
\n
\n

添加库文件到自己的模板

lib目录-1

在库文件找到下面的文件拷贝到自己刚建立的lib/CMSIS目录

\n

添加完成后 lib/CMSIS目录是这样:

\n

\"image-20210910201435838\"

\n

这些文件去哪里找:在库文件的 【Libraries】目录下

\n

内核通用源文件

\n

\"image-20210910200517197\"

\n

启动文件,只需要红框圈起来的那个,对应 stm32f103ZE开发板芯片

\n

\"image-20210910200757380\"

\n

系统初始化文件

\n

\"image-20210910201253535\"

\n
lib目录-2

去固件库的【Libraries】文件夹下【如下图】把文件夹STM32F10x_StdPeriph_Driver拷贝到自己建立的 lib 目录下

\n

\"image-20210910201957385\"

\n
user目录

这是添加完成后的user目录:

\n

\"image-20210910202442964\"

\n

这些文件在哪里找:

\n
    \n
  • main.c: 可以自己新建一个空的main.c文件,也可以去官方模板那里拷贝过来(官方模板在 /Project/STM32F10x_StdPeriph_Template/目录下, 下面第1幅图)
  • \n
  • 最下面的三个stm32f10x_conf.hstm32f10x_it.cstm32f10x_it.h: 去官方模板拷贝
  • \n
  • 第二个stm32f10x.h: 在库的Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x路径下找(下面第2幅图)
  • \n
\n

\"image-20210910202705273\"

\n

\"image-20210910203037259\"

\n

注意,以上步骤只是在磁盘上建立了这么一个目录结构,keil还不知道有这个工程,所以我们要在keil新建一个工程,然后把这个工程与存放在磁盘上的目录(还不能叫工程)进行关联,这样才能形成一个完整结构的工程。

\n
\n

打开mdk keil新建stm32工程

这个步骤是为了在keil中新建一个工程与存放在磁盘上的目录(还不能叫工程)进行关联映射,所以有2步:1-新建工程, 2-进行关联

\n

新建工程

注意新建工程时,有几个步骤:

\n
    \n
  1. 工程名,任意(根据自己的命名风格,不能有中文)
  2. \n
  3. 选择芯片,这里使用 STM32F103ZE
  4. \n
  5. 选择库,这里先不选,后面再选,直接跳过
  6. \n
\n
具体步骤 \n
\n

点击 new uVision Project

\"image-20210910210845401\"

自己命名,选择保存;

选择芯片 STM32F103ZE

\"image-20210910211314033\"

选择库,直接跳过,后面再选

\"image-20210910211418672\"

完成

\"image-20210910211508362\"

\n
\n
\n

完善工程结构

这里要把这个工程与磁盘上的目录和文件进行映射关联

\n

选择工程管理,如下:

\n

\"image-20210910212307092\"

\n

依次新建这几个目录:

\n
    \n
  • user: 存放用户代码文件
  • \n
  • startup: 存放启动文件
  • \n
  • CMSIS: 内核库文件
  • \n
  • StdPeriph_Driver: 外设库文件
  • \n
\n

\"image-20210910212703182\"

\n

分别往这几个目录添加文件:

\n

\"image-20210910214210061\"

\n

user(从 /user/目录下选择)

\n
    \n
  • main.c
  • \n
  • stm32f10x_it.c
  • \n
\n

startup(从 /lib/CMSIS/ 目录下选择)

\n
    \n
  • startup_stm32f10x_hd.s(注意添加时没显示出来要选择 文件类型为 “All files(*.*)“)
  • \n
\n

CMSIS(从 /lib/CMSIS/目录下选择)

\n
    \n
  • core_cm3.c
  • \n
  • system_stm32f10x.c
  • \n
\n

StdPeriph_Driver(从/lib/STM32F10x_StdPeriph_Driver/src/目录下选择,可以选择全部,也可以只选择下面两个,根据自己需要,下面这2个一般都会用到,所以都添加进来)

\n
    \n
  • stm32f10x_rcc.c
  • \n
  • stm32f10x_gpio.c
  • \n
\n

添加完成如下:

\n

\"image-20210910214412880\"

\n

到此工程结构已经建立完成,可以在main.c文件下写代码了,但是现在还不能编译,因为编译需要的头文件还没包含进来,以及编译时的其它一些配置还没完成

\n
\n

编译配置

选择这个魔法棒

\n

\"image-20210910215321021\"

\n

配置Target项

\n

\"image-20210910215245013\"

\n

配置output项

\n

\"image-20210910220000653\"

\n

配置 listing项

\n

\"image-20210910220055189\"

\n

配置 C/C++ 项,目的是为了把头文件添加进来

\n

下图的第2步添加的是:USE_STDPERIPH_DRIVER,STM32F10X_HD(注意中间有个英文逗号),表示使用外部库函数驱动

\n

\"image-20210910220625228\"

\n

配置Debug项:ARM仿真器选项配置(手中有开发板可以先通过ARM仿真器连接电脑)

\n

\"image-20210910221520551\"

\n

下载配置(在上一步的Debugger配置中:Debugger->Settings->FlashDownload)

\n

\"image-20210910221912505\"

\n

到此所有配置完成,选择ok,可以愉快的写代码了

\n
\n

测试

打开main.c文件,删除原有代码(如果有的话),添加如下代码:

\n
1
2
3
4
5
6
7
8
9
#include "stm32f10x.h"

int main(){
\t
\twhile(1){

\t}
}

\n

编译运行,没有报错,完美。。。。

\n

\"image-20210910222416127\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

工程结构建立

初始化工程目录结构

首先创建一个文件夹test, 用于存放该工程,然后在该文件夹下创建以下几个文件夹(目的是让工程管理起来更有条理):

\n
    \n
  • lib: 用于存放库文件(像Libraries、Lib等文件名字也是可以的,根据自己的风格,最好是能”见名知义”)
      \n
    • CMSIS: 存放内核核心库文件
    • \n
    • STM32F10x_StdPeriph_Driver: 是STM32Fl0x标准外设驱动库函数目录,存放STM32F10x微控制器的外设驱动(这个文件夹可以不建立,等下直接把库的拷贝过来)
    • \n
    \n
  • \n
  • obj: 用于存放程序编译输出的各种文件
  • \n
  • user: 用于存放main.c等用户自己的文件
  • \n
\n

添加相关文件

固件库说明

展开 \n
\n

首先要去官网或其他渠道下载STM32固件库,这个库包含了我们创建要用到的所有文件,其包含的文件如下

\"image-20210910194646565\"

【Libraries】文件夹存放STM32F10x开发要用到的各种库函数和启动文件,包括CMSISSTM32F10x_StdPeriph_Driver两个子文件夹:

  1. 【CMSIS】文件夹是内核库核心文件夹,包括CoreSupport和DeviceSupport等两个文件夹

    CoreSupport文件夹包括Cortcx-M3内核通用源文件core_cm3.c和头文件core_cm3.h

  2. 【DeviceSupport】文件夹STM32F0x头文件stm32f10x.h和系统初始化文件system_stm32f10x.c【STM32F10x_StdPeriph_Driver】文件夹是STM32Fl0x标准外设驱动库函数目录,包括了所有STM32F10x微控制器的外设驱动

\"image-20210910195021702\"

【Project】文件夹对应STM32F10x标准外设库体系架构中的用户层,用来存放ST官方提供的STM32F10x工程模板和外设驱动示例

【Utilities】文件夹用于存放ST官方评估板的BSP(Board Support Package, 板级支持包)和额外的第三方固件

\n
\n
\n

添加库文件到自己的模板

lib目录-1

在库文件找到下面的文件拷贝到自己刚建立的lib/CMSIS目录

\n

添加完成后 lib/CMSIS目录是这样:

\n

\"image-20210910201435838\"

\n

这些文件去哪里找:在库文件的 【Libraries】目录下

\n

内核通用源文件

\n

\"image-20210910200517197\"

\n

启动文件,只需要红框圈起来的那个,对应 stm32f103ZE开发板芯片

\n

\"image-20210910200757380\"

\n

系统初始化文件

\n

\"image-20210910201253535\"

\n
lib目录-2

去固件库的【Libraries】文件夹下【如下图】把文件夹STM32F10x_StdPeriph_Driver拷贝到自己建立的 lib 目录下

\n

\"image-20210910201957385\"

\n
user目录

这是添加完成后的user目录:

\n

\"image-20210910202442964\"

\n

这些文件在哪里找:

\n
    \n
  • main.c: 可以自己新建一个空的main.c文件,也可以去官方模板那里拷贝过来(官方模板在 /Project/STM32F10x_StdPeriph_Template/目录下, 下面第1幅图)
  • \n
  • 最下面的三个stm32f10x_conf.hstm32f10x_it.cstm32f10x_it.h: 去官方模板拷贝
  • \n
  • 第二个stm32f10x.h: 在库的Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x路径下找(下面第2幅图)
  • \n
\n

\"image-20210910202705273\"

\n

\"image-20210910203037259\"

\n

注意,以上步骤只是在磁盘上建立了这么一个目录结构,keil还不知道有这个工程,所以我们要在keil新建一个工程,然后把这个工程与存放在磁盘上的目录(还不能叫工程)进行关联,这样才能形成一个完整结构的工程。

\n
\n

打开mdk keil新建stm32工程

这个步骤是为了在keil中新建一个工程与存放在磁盘上的目录(还不能叫工程)进行关联映射,所以有2步:1-新建工程, 2-进行关联

\n

新建工程

注意新建工程时,有几个步骤:

\n
    \n
  1. 工程名,任意(根据自己的命名风格,不能有中文)
  2. \n
  3. 选择芯片,这里使用 STM32F103ZE
  4. \n
  5. 选择库,这里先不选,后面再选,直接跳过
  6. \n
\n
具体步骤 \n
\n

点击 new uVision Project

\"image-20210910210845401\"

自己命名,选择保存;

选择芯片 STM32F103ZE

\"image-20210910211314033\"

选择库,直接跳过,后面再选

\"image-20210910211418672\"

完成

\"image-20210910211508362\"

\n
\n
\n

完善工程结构

这里要把这个工程与磁盘上的目录和文件进行映射关联

\n

选择工程管理,如下:

\n

\"image-20210910212307092\"

\n

依次新建这几个目录:

\n
    \n
  • user: 存放用户代码文件
  • \n
  • startup: 存放启动文件
  • \n
  • CMSIS: 内核库文件
  • \n
  • StdPeriph_Driver: 外设库文件
  • \n
\n

\"image-20210910212703182\"

\n

分别往这几个目录添加文件:

\n

\"image-20210910214210061\"

\n

user(从 /user/目录下选择)

\n
    \n
  • main.c
  • \n
  • stm32f10x_it.c
  • \n
\n

startup(从 /lib/CMSIS/ 目录下选择)

\n
    \n
  • startup_stm32f10x_hd.s(注意添加时没显示出来要选择 文件类型为 “All files(*.*)“)
  • \n
\n

CMSIS(从 /lib/CMSIS/目录下选择)

\n
    \n
  • core_cm3.c
  • \n
  • system_stm32f10x.c
  • \n
\n

StdPeriph_Driver(从/lib/STM32F10x_StdPeriph_Driver/src/目录下选择,可以选择全部,也可以只选择下面两个,根据自己需要,下面这2个一般都会用到,所以都添加进来)

\n
    \n
  • stm32f10x_rcc.c
  • \n
  • stm32f10x_gpio.c
  • \n
\n

添加完成如下:

\n

\"image-20210910214412880\"

\n

到此工程结构已经建立完成,可以在main.c文件下写代码了,但是现在还不能编译,因为编译需要的头文件还没包含进来,以及编译时的其它一些配置还没完成

\n
\n

编译配置

选择这个魔法棒

\n

\"image-20210910215321021\"

\n

配置Target项

\n

\"image-20210910215245013\"

\n

配置output项

\n

\"image-20210910220000653\"

\n

配置 listing项

\n

\"image-20210910220055189\"

\n

配置 C/C++ 项,目的是为了把头文件添加进来

\n

下图的第2步添加的是:USE_STDPERIPH_DRIVER,STM32F10X_HD(注意中间有个英文逗号),表示使用外部库函数驱动

\n

\"image-20210910220625228\"

\n

配置Debug项:ARM仿真器选项配置(手中有开发板可以先通过ARM仿真器连接电脑)

\n

\"image-20210910221520551\"

\n

下载配置(在上一步的Debugger配置中:Debugger->Settings->FlashDownload)

\n

\"image-20210910221912505\"

\n

到此所有配置完成,选择ok,可以愉快的写代码了

\n
\n

测试

打开main.c文件,删除原有代码(如果有的话),添加如下代码:

\n
1
2
3
4
5
6
7
8
9
#include "stm32f10x.h"

int main(){
\t
\twhile(1){

\t}
}

\n

编译运行,没有报错,完美。。。。

\n

\"image-20210910222416127\"

\n"},{"date":"2021-09-11T08:41:28.000Z","title":"stm32第一个程序","description":"使用stm32库函数编写程序点亮led灯","cover":"https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png","abbrlink":"f8dae6c5","_content":"\n\n\n## 新建工程\n\n看这篇[文章](/posts/190b30be.html)\n\n建立完成后工程结构如图:\n\n![image-20210912001909108](https://gitee.com/ajream/images/raw/master/img/20210912001916_image-20210912001909108.png)\n\n## 编写main.c文件\n\n```c\n#include \"stm32f10x.h\"\n\nvoid ledConfig(void);\nvoid ledOn(void);\nvoid ledOff(void);\nvoid delay(unsigned long x);\n\nint main(){\n\t\n\tledConfig();\n\t\n\twhile(1){\n\t\n\t\tledOn();\n\t\tdelay(0x5FFFFF);\n\t\tledOff();\n\t\tdelay(0x5FFFFF);\n\t}\n}\n\n\nvoid ledConfig(void){\n\tGPIO_InitTypeDef GPIO_InitStructure;\n\tRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);\n\tGPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;\n\tGPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;\n\tGPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;\n\tGPIO_Init(GPIOA, &GPIO_InitStructure);\n\n}\n\nvoid ledOn(){\n\tGPIO_ResetBits(GPIOA, GPIO_Pin_8);\n}\n\nvoid ledOff(){\n\n\tGPIO_SetBits(GPIOA, GPIO_Pin_8);\n\n}\n\nvoid delay(unsigned long x){\n\tunsigned long i;\n\tfor(i=0; iStart/Stop Debug Session或工具栏Debug按钮进入调试模式\n\n2. 打开相关窗口添加监测变量或信号\n\n ![image-20210912003102247](https://gitee.com/ajream/images/raw/master/img/20210912003103_image-20210912003102247.png)\n\n 点击setup按钮,在弹出的窗口添加监测变量 `PORTA.8`(表示GPIOA_Pin_8)\n\n ![image-20210912003207021](https://gitee.com/ajream/images/raw/master/img/20210912003208_image-20210912003207021.png)\n\n ![image-20210912003445343](https://gitee.com/ajream/images/raw/master/img/20210912003446_image-20210912003445343.png)\n\n3. 开始运行\n\n ![image-20210912003634281](https://gitee.com/ajream/images/raw/master/img/20210912003635_image-20210912003634281.png)\n\n4. 查看仿真结果\n\n 把代码编辑区下拉后可以看见输出波形图\n\n ![image-20210912003807825](https://gitee.com/ajream/images/raw/master/img/20210912003809_image-20210912003807825.png)\n\n5. 关闭仿真\n\n ![image-20210912003856907](https://gitee.com/ajream/images/raw/master/img/20210912003858_image-20210912003856907.png)\n\n","source":"_posts/stm32/stm32点亮led灯程序.md","raw":"---\ndate: '2021-09-11 16:41:28'\ntitle: stm32第一个程序\ntags:\n - stm32\ncategories:\n - 硬件学习\n - stm32\ndescription: 使用stm32库函数编写程序点亮led灯\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png'\nabbrlink: f8dae6c5\n---\n\n\n\n## 新建工程\n\n看这篇[文章](/posts/190b30be.html)\n\n建立完成后工程结构如图:\n\n![image-20210912001909108](https://gitee.com/ajream/images/raw/master/img/20210912001916_image-20210912001909108.png)\n\n## 编写main.c文件\n\n```c\n#include \"stm32f10x.h\"\n\nvoid ledConfig(void);\nvoid ledOn(void);\nvoid ledOff(void);\nvoid delay(unsigned long x);\n\nint main(){\n\t\n\tledConfig();\n\t\n\twhile(1){\n\t\n\t\tledOn();\n\t\tdelay(0x5FFFFF);\n\t\tledOff();\n\t\tdelay(0x5FFFFF);\n\t}\n}\n\n\nvoid ledConfig(void){\n\tGPIO_InitTypeDef GPIO_InitStructure;\n\tRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);\n\tGPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;\n\tGPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;\n\tGPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;\n\tGPIO_Init(GPIOA, &GPIO_InitStructure);\n\n}\n\nvoid ledOn(){\n\tGPIO_ResetBits(GPIOA, GPIO_Pin_8);\n}\n\nvoid ledOff(){\n\n\tGPIO_SetBits(GPIOA, GPIO_Pin_8);\n\n}\n\nvoid delay(unsigned long x){\n\tunsigned long i;\n\tfor(i=0; iStart/Stop Debug Session或工具栏Debug按钮进入调试模式\n\n2. 打开相关窗口添加监测变量或信号\n\n ![image-20210912003102247](https://gitee.com/ajream/images/raw/master/img/20210912003103_image-20210912003102247.png)\n\n 点击setup按钮,在弹出的窗口添加监测变量 `PORTA.8`(表示GPIOA_Pin_8)\n\n ![image-20210912003207021](https://gitee.com/ajream/images/raw/master/img/20210912003208_image-20210912003207021.png)\n\n ![image-20210912003445343](https://gitee.com/ajream/images/raw/master/img/20210912003446_image-20210912003445343.png)\n\n3. 开始运行\n\n ![image-20210912003634281](https://gitee.com/ajream/images/raw/master/img/20210912003635_image-20210912003634281.png)\n\n4. 查看仿真结果\n\n 把代码编辑区下拉后可以看见输出波形图\n\n ![image-20210912003807825](https://gitee.com/ajream/images/raw/master/img/20210912003809_image-20210912003807825.png)\n\n5. 关闭仿真\n\n ![image-20210912003856907](https://gitee.com/ajream/images/raw/master/img/20210912003858_image-20210912003856907.png)\n\n","slug":"stm32/stm32点亮led灯程序","published":1,"updated":"2021-09-12T01:53:14.761Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6re0086akve1a6d4ffa","content":"

新建工程

看这篇文章

\n

建立完成后工程结构如图:

\n

\"image-20210912001909108\"

\n

编写main.c文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "stm32f10x.h"

void ledConfig(void);
void ledOn(void);
void ledOff(void);
void delay(unsigned long x);

int main(){
\t
\tledConfig();
\t
\twhile(1){
\t
\t\tledOn();
\t\tdelay(0x5FFFFF);
\t\tledOff();
\t\tdelay(0x5FFFFF);
\t}
}


void ledConfig(void){
\tGPIO_InitTypeDef GPIO_InitStructure;
\tRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
\tGPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
\tGPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
\tGPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
\tGPIO_Init(GPIOA, &GPIO_InitStructure);

}

void ledOn(){
\tGPIO_ResetBits(GPIOA, GPIO_Pin_8);
}

void ledOff(){

\tGPIO_SetBits(GPIOA, GPIO_Pin_8);

}

void delay(unsigned long x){
\tunsigned long i;
\tfor(i=0; i<x; ++i);
}


\n

仿真调试

调试前配置

按图中顺序修改配置,其中第4、5步为:

\n
    \n
  • CPU DLL: SARMCM3.DLL, Parameter为空
  • \n
  • Dialog DLL: DARMSTM.DLL, Parameter: -pSTM32F103ZE
  • \n
\n

\"image-20210912002225533\"

\n

进入调试模式

    \n
  1. 选择菜单栏 Debug->Start/Stop Debug Session或工具栏Debug按钮进入调试模式

    \n
  2. \n
  3. 打开相关窗口添加监测变量或信号

    \n

    \"image-20210912003102247\"

    \n

    点击setup按钮,在弹出的窗口添加监测变量 PORTA.8(表示GPIOA_Pin_8)

    \n

    \"image-20210912003207021\"

    \n

    \"image-20210912003445343\"

    \n
  4. \n
  5. 开始运行

    \n

    \"image-20210912003634281\"

    \n
  6. \n
  7. 查看仿真结果

    \n

    把代码编辑区下拉后可以看见输出波形图

    \n

    \"image-20210912003807825\"

    \n
  8. \n
  9. 关闭仿真

    \n

    \"image-20210912003856907\"

    \n
  10. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

新建工程

看这篇文章

\n

建立完成后工程结构如图:

\n

\"image-20210912001909108\"

\n

编写main.c文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "stm32f10x.h"

void ledConfig(void);
void ledOn(void);
void ledOff(void);
void delay(unsigned long x);

int main(){
\t
\tledConfig();
\t
\twhile(1){
\t
\t\tledOn();
\t\tdelay(0x5FFFFF);
\t\tledOff();
\t\tdelay(0x5FFFFF);
\t}
}


void ledConfig(void){
\tGPIO_InitTypeDef GPIO_InitStructure;
\tRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
\tGPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
\tGPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
\tGPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
\tGPIO_Init(GPIOA, &GPIO_InitStructure);

}

void ledOn(){
\tGPIO_ResetBits(GPIOA, GPIO_Pin_8);
}

void ledOff(){

\tGPIO_SetBits(GPIOA, GPIO_Pin_8);

}

void delay(unsigned long x){
\tunsigned long i;
\tfor(i=0; i<x; ++i);
}


\n

仿真调试

调试前配置

按图中顺序修改配置,其中第4、5步为:

\n
    \n
  • CPU DLL: SARMCM3.DLL, Parameter为空
  • \n
  • Dialog DLL: DARMSTM.DLL, Parameter: -pSTM32F103ZE
  • \n
\n

\"image-20210912002225533\"

\n

进入调试模式

    \n
  1. 选择菜单栏 Debug->Start/Stop Debug Session或工具栏Debug按钮进入调试模式

    \n
  2. \n
  3. 打开相关窗口添加监测变量或信号

    \n

    \"image-20210912003102247\"

    \n

    点击setup按钮,在弹出的窗口添加监测变量 PORTA.8(表示GPIOA_Pin_8)

    \n

    \"image-20210912003207021\"

    \n

    \"image-20210912003445343\"

    \n
  4. \n
  5. 开始运行

    \n

    \"image-20210912003634281\"

    \n
  6. \n
  7. 查看仿真结果

    \n

    把代码编辑区下拉后可以看见输出波形图

    \n

    \"image-20210912003807825\"

    \n
  8. \n
  9. 关闭仿真

    \n

    \"image-20210912003856907\"

    \n
  10. \n
\n"},{"title":"typora图床配置","description":"使用国内Gitee仓库作为typora图床,提高访问速度","abbrlink":"8b75069d","date":"2021-09-13T12:10:12.000Z","cover":"/img/articles2.png","_content":"\n\n\n\n\n\n\n## 下载相关软件\n\n1. 下载nodejs(自行百度)\n\n2. 安装picgo.exe \n\n 打开typora后,选择 【文件】-【偏好设置】-【图像】,看下图:\n\n \"image-20210419161251100\"\n\n3. 下载插件 gitee-uploader和super-prefix\n\n```sh\n#先切换到picgo安装目录下:cd C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>\nC:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>picgo install gitee-uploader super-prefix\n```\n\n\n\n## gitee配置\n\n1. 在gitee中创建仓库images(必须是public的)以及文件夹img\n\n2. 在gitee中获取私有令牌:\n\n - 在【设置】-【安全设置:私人令牌】-【生成新令牌】\n\n ![在这里插入图片描述](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-35-58_2020101721463059.png)\n\n - 提交之后把 token 复制下来,这个 token 只会出现这一次,丢了就再生成新的令牌\n\n\n\n## picgo插件配置\n\n该配置文件**config.json**在 目录 `C:\\Users\\用户名\\.picgo` 下,刚才下载的插件也在这个目录\n\n![image-20210419154225236](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-42-28_image-20210419154225236.png)\n\n\n\n\n\n打开配置文件config.json,复制下面内容,把原来文件的所有内容覆盖掉\n\n```json\n{\n \"picBed\": {\n \"uploader\": \"gitee\",\n \"current\": \"gitee\",\n \"transformer\": \"path\",\n \"gitee\": {\n \"repo\": \"用户名/images\", // 需要改,仓库名,格式是 user/repo 必填\n \"token\": \"ed1234567890\", // 需要改,gitee 私人令牌 必填(改为自己的)\n \"path\": \"img/\", // 需要改,自定义存储路径,比如 img/ ,但是前提是仓库有这个文件路径,如 user/repo/img\n \"customUrl\": \"\", // 没有自己的域名的话,可以默认为空就行; 如果自定义域名,注意要加http://或者https://,\n \"branch\": \"\" // 分支名,默认是 master\n }\n },\n \"picgoPlugins\": {\n \"picgo-plugin-gitee-uploader\": true,\n \"picgo-plugin-super-prefix\": true\n },\n \"picgo-plugin-super-prefix\": {\n \"prefixFormat\": \"YYYY-MM-DD HH-mm-ss_\" //图片文件名前缀,这么写表示以图片创建时间来命名,注意不能用冒号\n }, //super-prefix插件配置: prefixFormat 或者 fileFormat\n \"picgo-plugin-gitee-uploader\": {\n \"lastSync\": \"\"\n }\n}\n```\n\n需要改的几个地方\n\n1. repo:改为自己的仓库,格式为:\n\n ```json\n \"username/仓库名\"\n \n 如:\n \"xiaojing/images\"\n ```\n\n2. token: gitee的私人令牌,刚刚叫你复制的\n\n3. path:自己仓库下的一个文件夹,例如在仓库images下创建了文件夹img,则为\n\n ```json\n \"img/\"\n ```\n\n\n\n\n\n## typora配置\n\n进入相关设置:【文件】-【偏好设置】-【图像】\n\n![image-20210419155940813](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-59-43_image-20210419155940813.png)\n\n验证成功画面:\n\n\"image-20210419162212020\"\n\n\n\n> 到此配置完成,之后在typora中粘贴图片就会自动上传\n\n","source":"_posts/杂七杂八/gitee图床配置.md","raw":"---\ntitle: typora图床配置\ntags:\n - 图床\ncategories: 杂七杂八\ndescription: 使用国内Gitee仓库作为typora图床,提高访问速度\nabbrlink: 8b75069d\ndate: 2021-09-13 20:10:12\ncover:\n---\n\n\n\n\n\n\n\n## 下载相关软件\n\n1. 下载nodejs(自行百度)\n\n2. 安装picgo.exe \n\n 打开typora后,选择 【文件】-【偏好设置】-【图像】,看下图:\n\n \"image-20210419161251100\"\n\n3. 下载插件 gitee-uploader和super-prefix\n\n```sh\n#先切换到picgo安装目录下:cd C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>\nC:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>picgo install gitee-uploader super-prefix\n```\n\n\n\n## gitee配置\n\n1. 在gitee中创建仓库images(必须是public的)以及文件夹img\n\n2. 在gitee中获取私有令牌:\n\n - 在【设置】-【安全设置:私人令牌】-【生成新令牌】\n\n ![在这里插入图片描述](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-35-58_2020101721463059.png)\n\n - 提交之后把 token 复制下来,这个 token 只会出现这一次,丢了就再生成新的令牌\n\n\n\n## picgo插件配置\n\n该配置文件**config.json**在 目录 `C:\\Users\\用户名\\.picgo` 下,刚才下载的插件也在这个目录\n\n![image-20210419154225236](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-42-28_image-20210419154225236.png)\n\n\n\n\n\n打开配置文件config.json,复制下面内容,把原来文件的所有内容覆盖掉\n\n```json\n{\n \"picBed\": {\n \"uploader\": \"gitee\",\n \"current\": \"gitee\",\n \"transformer\": \"path\",\n \"gitee\": {\n \"repo\": \"用户名/images\", // 需要改,仓库名,格式是 user/repo 必填\n \"token\": \"ed1234567890\", // 需要改,gitee 私人令牌 必填(改为自己的)\n \"path\": \"img/\", // 需要改,自定义存储路径,比如 img/ ,但是前提是仓库有这个文件路径,如 user/repo/img\n \"customUrl\": \"\", // 没有自己的域名的话,可以默认为空就行; 如果自定义域名,注意要加http://或者https://,\n \"branch\": \"\" // 分支名,默认是 master\n }\n },\n \"picgoPlugins\": {\n \"picgo-plugin-gitee-uploader\": true,\n \"picgo-plugin-super-prefix\": true\n },\n \"picgo-plugin-super-prefix\": {\n \"prefixFormat\": \"YYYY-MM-DD HH-mm-ss_\" //图片文件名前缀,这么写表示以图片创建时间来命名,注意不能用冒号\n }, //super-prefix插件配置: prefixFormat 或者 fileFormat\n \"picgo-plugin-gitee-uploader\": {\n \"lastSync\": \"\"\n }\n}\n```\n\n需要改的几个地方\n\n1. repo:改为自己的仓库,格式为:\n\n ```json\n \"username/仓库名\"\n \n 如:\n \"xiaojing/images\"\n ```\n\n2. token: gitee的私人令牌,刚刚叫你复制的\n\n3. path:自己仓库下的一个文件夹,例如在仓库images下创建了文件夹img,则为\n\n ```json\n \"img/\"\n ```\n\n\n\n\n\n## typora配置\n\n进入相关设置:【文件】-【偏好设置】-【图像】\n\n![image-20210419155940813](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-59-43_image-20210419155940813.png)\n\n验证成功画面:\n\n\"image-20210419162212020\"\n\n\n\n> 到此配置完成,之后在typora中粘贴图片就会自动上传\n\n","slug":"杂七杂八/gitee图床配置","published":1,"updated":"2021-09-13T12:30:38.795Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rf008aakve5eeddxx1","content":"

下载相关软件

    \n
  1. 下载nodejs(自行百度)

    \n
  2. \n
  3. 安装picgo.exe

    \n

    打开typora后,选择 【文件】-【偏好设置】-【图像】,看下图:

    \n

    \"image-20210419161251100\"

    \n
  4. \n
  5. 下载插件 gitee-uploader和super-prefix

    \n
  6. \n
\n
1
2
#先切换到picgo安装目录下:cd C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>
C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>picgo install gitee-uploader super-prefix
\n

gitee配置

    \n
  1. 在gitee中创建仓库images(必须是public的)以及文件夹img

    \n
  2. \n
  3. 在gitee中获取私有令牌:

    \n
      \n
    • 在【设置】-【安全设置:私人令牌】-【生成新令牌】
    • \n
    \n

    \"在这里插入图片描述\"

    \n
      \n
    • 提交之后把 token 复制下来,这个 token 只会出现这一次,丢了就再生成新的令牌
    • \n
    \n
  4. \n
\n

picgo插件配置

该配置文件config.json在 目录 C:\\Users\\用户名\\.picgo 下,刚才下载的插件也在这个目录

\n

\"image-20210419154225236\"

\n

打开配置文件config.json,复制下面内容,把原来文件的所有内容覆盖掉

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"picBed": {
"uploader": "gitee",
"current": "gitee",
"transformer": "path",
"gitee": {
"repo": "用户名/images", // 需要改,仓库名,格式是 user/repo 必填
"token": "ed1234567890", // 需要改,gitee 私人令牌 必填(改为自己的)
"path": "img/", // 需要改,自定义存储路径,比如 img/ ,但是前提是仓库有这个文件路径,如 user/repo/img
"customUrl": "", // 没有自己的域名的话,可以默认为空就行; 如果自定义域名,注意要加http://或者https://,
"branch": "" // 分支名,默认是 master
}
},
"picgoPlugins": {
"picgo-plugin-gitee-uploader": true,
"picgo-plugin-super-prefix": true
},
"picgo-plugin-super-prefix": {
"prefixFormat": "YYYY-MM-DD HH-mm-ss_" //图片文件名前缀,这么写表示以图片创建时间来命名,注意不能用冒号
}, //super-prefix插件配置: prefixFormat 或者 fileFormat
"picgo-plugin-gitee-uploader": {
"lastSync": ""
}
}
\n

需要改的几个地方

\n
    \n
  1. repo:改为自己的仓库,格式为:

    \n
    1
    2
    3
    4
    "username/仓库名"

    如:
    "xiaojing/images"
    \n
  2. \n
  3. token: gitee的私人令牌,刚刚叫你复制的

    \n
  4. \n
  5. path:自己仓库下的一个文件夹,例如在仓库images下创建了文件夹img,则为

    \n
    1
    "img/"
    \n
  6. \n
\n

typora配置

进入相关设置:【文件】-【偏好设置】-【图像】

\n

\"image-20210419155940813\"

\n

验证成功画面:

\n

\"image-20210419162212020\"

\n
\n

到此配置完成,之后在typora中粘贴图片就会自动上传

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

下载相关软件

    \n
  1. 下载nodejs(自行百度)

    \n
  2. \n
  3. 安装picgo.exe

    \n

    打开typora后,选择 【文件】-【偏好设置】-【图像】,看下图:

    \n

    \"image-20210419161251100\"

    \n
  4. \n
  5. 下载插件 gitee-uploader和super-prefix

    \n
  6. \n
\n
1
2
#先切换到picgo安装目录下:cd C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>
C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>picgo install gitee-uploader super-prefix
\n

gitee配置

    \n
  1. 在gitee中创建仓库images(必须是public的)以及文件夹img

    \n
  2. \n
  3. 在gitee中获取私有令牌:

    \n
      \n
    • 在【设置】-【安全设置:私人令牌】-【生成新令牌】
    • \n
    \n

    \"在这里插入图片描述\"

    \n
      \n
    • 提交之后把 token 复制下来,这个 token 只会出现这一次,丢了就再生成新的令牌
    • \n
    \n
  4. \n
\n

picgo插件配置

该配置文件config.json在 目录 C:\\Users\\用户名\\.picgo 下,刚才下载的插件也在这个目录

\n

\"image-20210419154225236\"

\n

打开配置文件config.json,复制下面内容,把原来文件的所有内容覆盖掉

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"picBed": {
"uploader": "gitee",
"current": "gitee",
"transformer": "path",
"gitee": {
"repo": "用户名/images", // 需要改,仓库名,格式是 user/repo 必填
"token": "ed1234567890", // 需要改,gitee 私人令牌 必填(改为自己的)
"path": "img/", // 需要改,自定义存储路径,比如 img/ ,但是前提是仓库有这个文件路径,如 user/repo/img
"customUrl": "", // 没有自己的域名的话,可以默认为空就行; 如果自定义域名,注意要加http://或者https://,
"branch": "" // 分支名,默认是 master
}
},
"picgoPlugins": {
"picgo-plugin-gitee-uploader": true,
"picgo-plugin-super-prefix": true
},
"picgo-plugin-super-prefix": {
"prefixFormat": "YYYY-MM-DD HH-mm-ss_" //图片文件名前缀,这么写表示以图片创建时间来命名,注意不能用冒号
}, //super-prefix插件配置: prefixFormat 或者 fileFormat
"picgo-plugin-gitee-uploader": {
"lastSync": ""
}
}
\n

需要改的几个地方

\n
    \n
  1. repo:改为自己的仓库,格式为:

    \n
    1
    2
    3
    4
    "username/仓库名"

    如:
    "xiaojing/images"
    \n
  2. \n
  3. token: gitee的私人令牌,刚刚叫你复制的

    \n
  4. \n
  5. path:自己仓库下的一个文件夹,例如在仓库images下创建了文件夹img,则为

    \n
    1
    "img/"
    \n
  6. \n
\n

typora配置

进入相关设置:【文件】-【偏好设置】-【图像】

\n

\"image-20210419155940813\"

\n

验证成功画面:

\n

\"image-20210419162212020\"

\n
\n

到此配置完成,之后在typora中粘贴图片就会自动上传

\n
\n"},{"title":"矢量场","notes":"电磁场","katex":true,"description":"矢量运算","abbrlink":"401491fc","date":"2021-09-08T08:00:00.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210902230207_dianci.png","_content":"\n\n### 矢量运算\n\n矢量加法、减法、数乘跳过\n\n#### 矢量的标量积(点积)\n$$\\vec{A} \\cdot \\vec{B} = |A||B|cos\\theta$$\n\n特点:相互垂直的两个矢量的标量积为0\n\n矢量点积计算\n\n$$\n\\vec{A} \\cdot \\vec{B} = A_xB_x\\vec{a_x} + A_yB_y\\vec{a_y} + A_zB_z\\vec{a_z}\n$$\n\n#### 矢量的矢量积(叉积)\n$$\\vec{C}=\\vec{A} \\times \\vec{B} = |A||B|sin\\theta ~ \\vec{a}~~~~~~~~~~~~~~~~~~0\\leq\\theta\\leq\\pi$$ \n特点:相互平行的两个矢量的矢量积为0\n\n\n\n\n矢量的叉积计算\n$$\n\\vec{A} \\times \\vec{B} =\n\\begin{vmatrix} \n\\vec{a_x} & \\vec{a_y} & \\vec{a_z} \\\\\nA_x & A_y & A_z \\\\\nB_x&B_y&B_z \\\\\n\\end{vmatrix}\n$$\n\n\n\n### 矢量微元\n\n直角坐标系\n\n","source":"_posts/电磁场/矢量场.md","raw":"---\ntitle: 矢量场\ntags:\n - 矢量\ncategories:\n - 电磁场\n - 矢量\nnotes: 电磁场\nkatex: true\ndescription: 矢量运算\nabbrlink: 401491fc\ndate: 2021-09-08 16:00:00\ncover: https://gitee.com/ajream/images/raw/master/img/20210902230207_dianci.png\n---\n\n\n### 矢量运算\n\n矢量加法、减法、数乘跳过\n\n#### 矢量的标量积(点积)\n$$\\vec{A} \\cdot \\vec{B} = |A||B|cos\\theta$$\n\n特点:相互垂直的两个矢量的标量积为0\n\n矢量点积计算\n\n$$\n\\vec{A} \\cdot \\vec{B} = A_xB_x\\vec{a_x} + A_yB_y\\vec{a_y} + A_zB_z\\vec{a_z}\n$$\n\n#### 矢量的矢量积(叉积)\n$$\\vec{C}=\\vec{A} \\times \\vec{B} = |A||B|sin\\theta ~ \\vec{a}~~~~~~~~~~~~~~~~~~0\\leq\\theta\\leq\\pi$$ \n特点:相互平行的两个矢量的矢量积为0\n\n\n\n\n矢量的叉积计算\n$$\n\\vec{A} \\times \\vec{B} =\n\\begin{vmatrix} \n\\vec{a_x} & \\vec{a_y} & \\vec{a_z} \\\\\nA_x & A_y & A_z \\\\\nB_x&B_y&B_z \\\\\n\\end{vmatrix}\n$$\n\n\n\n### 矢量微元\n\n直角坐标系\n\n","slug":"电磁场/矢量场","published":1,"updated":"2021-09-08T09:14:19.444Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rg008cakve0mvn1u4x","content":"

矢量运算

矢量加法、减法、数乘跳过

\n

矢量的标量积(点积)

特点:相互垂直的两个矢量的标量积为0

\n

矢量点积计算

\n

矢量的矢量积(叉积)

特点:相互平行的两个矢量的矢量积为0

\n

矢量的叉积计算

\n

矢量微元

直角坐标系

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

矢量运算

矢量加法、减法、数乘跳过

\n

矢量的标量积(点积)

特点:相互垂直的两个矢量的标量积为0

\n

矢量点积计算

\n

矢量的矢量积(叉积)

特点:相互平行的两个矢量的矢量积为0

\n

矢量的叉积计算

\n

矢量微元

直角坐标系

\n"},{"title":"网络协议","description":"计算机网络协议protocol","cover":"https://gitee.com/ajream/images/raw/master/img/20210913084452_c-s.png","abbrlink":"1cd3002f","date":"2021-09-12T01:26:15.000Z","_content":"\n\n\n\n\n## 组成要素\n\n1. 语义: 语义是解释控制信息每个部分的意义。它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应\n\n2. 语法: 语法是用户数据与控制信息的结构与格式,以及数据出现的顺序\n\n3. 时序: 时序是对事件发生顺序的详细说明。(也可称为“同步”)\n\n\n\n{% note default modern %}\n人们形象地把这三个要素描述为:语义表示要做什么,语法表示要怎么做,时序表示做的顺序。\n{% endnote %}\n\n\n\n\n\n\n\n## OSI参考模型\n\n\n\n**OSI七层模型**(Open System Interconnect)即开放系统互连参考模型,是由**ISO**(International Organization for Standardization)**国际标准化组织**提出的,用于计算机或通信系统间互联的标准体系。\n\n\n\nOSI模型从上到下可分为七层,每一层都完成特定的功能,并为上一层提供服务,并使用下层所提供的服务。\n\n应用层:协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP\n\n表示层:数据的表示、安全、压缩。格式有,JPEG、ASCll、DECOIC、加密格式等\n\n会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话\n\n传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层\n\n网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP\n\n数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。\n\n物理层:建立、维护、断开物理连接。\n\n\n\n![OSImodel](https://gitee.com/ajream/images/raw/master/img/20210913093639_OSImodel.png)\n\n\n\n数据传输过程\n\n\n\n![img](https://upload-images.jianshu.io/upload_images/7541336-906a34d0af992f70.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp)\n\n\n\n## TCP-IP模型\n\n\n\nTCP/IP 模型是由 OSI 模型演化而来,TCP/IP 模型将 OSI 模型由七层简化为五层(一开始为四层),应用层、表示层、会话层统一为应用层。\n\n\n\n![image-20210913094813894](https://gitee.com/ajream/images/raw/master/img/20210913094816_image-20210913094813894.png)\n\n\n\n### 协议\n\nTCP/IP协议被称为传输控制协议/互联网协议,又称网络通讯协议(Transmission Control Protocol)。是由网络层的IP协议和传输层的TCP协议组成,是一个很大的协议集合。\n\n- 物理层和数据链路层没有定义任何特定协议,支持所有的标准和专用的协议。\n- 网络层定义了网络互联也就是IP协议,主要包括IP、ARP、RARP、ICMP、IGMP。\n- 传输层定义了TCP和UDP(User Datagram Protocol),我们会后面重点介绍一下TCP协议。\n- 应用层定义了HTTP(超文本传输协议)、FTP(文件传输协议)、DNS(域名系统)等协议。\n\n\n\n### 物理层\n\n计算机在传递数据的时候传递的都是0和1的数字,而物理层关心的是用什么信号来表示0和1,是否可以双向通信,最初的连接如何建立以及完成连接如何终止,总之,物理层是为数据传输提供可靠的环境。\n\n### 数据链路层\n\n数据链路层们于物理层和网络层之间,用来向网络层提供数据,就是把源计算机网络层传过来的信息传递给目标主机。\n数据链路层主要的作用包括:\n\n- 如何将数据组合成数据帧(Frame),帧是数据链路层的传输单位\n- 数据链路的建立、维护和拆除\n- 帧包装、帧传输、帧同步\n- 帧的差错恢复\n- 流量控制\n\n### 网络层\n\n网络层位于传输层和数据链路层之间,用于把数据从源主机经过若干个中间节点传送到目标主机,并向传输层提供最基础的数据传输服务,它要提供路由和选址的工作。\n\n### 传输层\n\n传输层主要提供以下几个功能\n\n1. 提供应用程序接口,为网络应用程序提供网络访问的途径;\n\n2. 提供可以从多个应用层序接收消息的功能(多路复用),同时也提供可以把消息分发给应用程序的功能(多路分解)。\n\n3. 对数据进行错误检测、流量控制。\n\n\n\n### 应用层\n\nTCP/IP的应用层对应于OSI的应用层、会话层、网络层,它们分别功能如下\n\n应用层:为用户的应用提供服务并支持网络访问。\n\n会话层:负责管理网络中计算之间的通信,提供传输层不具备的连接相关功能。\n\n表示层:负责转化数据格式,并处理数据加密和数据压缩。\n\n\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20201218111758350.png)","source":"_posts/网络通信/网络协议.md","raw":"---\ntitle: 网络协议\ntags:\n - 网络通信\ncategories:\n - 网络通信\ndescription: 计算机网络协议protocol\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210913084452_c-s.png'\nabbrlink: 1cd3002f\ndate: 2021-09-12 09:26:15\n---\n\n\n\n\n\n## 组成要素\n\n1. 语义: 语义是解释控制信息每个部分的意义。它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应\n\n2. 语法: 语法是用户数据与控制信息的结构与格式,以及数据出现的顺序\n\n3. 时序: 时序是对事件发生顺序的详细说明。(也可称为“同步”)\n\n\n\n{% note default modern %}\n人们形象地把这三个要素描述为:语义表示要做什么,语法表示要怎么做,时序表示做的顺序。\n{% endnote %}\n\n\n\n\n\n\n\n## OSI参考模型\n\n\n\n**OSI七层模型**(Open System Interconnect)即开放系统互连参考模型,是由**ISO**(International Organization for Standardization)**国际标准化组织**提出的,用于计算机或通信系统间互联的标准体系。\n\n\n\nOSI模型从上到下可分为七层,每一层都完成特定的功能,并为上一层提供服务,并使用下层所提供的服务。\n\n应用层:协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP\n\n表示层:数据的表示、安全、压缩。格式有,JPEG、ASCll、DECOIC、加密格式等\n\n会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话\n\n传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层\n\n网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP\n\n数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。\n\n物理层:建立、维护、断开物理连接。\n\n\n\n![OSImodel](https://gitee.com/ajream/images/raw/master/img/20210913093639_OSImodel.png)\n\n\n\n数据传输过程\n\n\n\n![img](https://upload-images.jianshu.io/upload_images/7541336-906a34d0af992f70.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp)\n\n\n\n## TCP-IP模型\n\n\n\nTCP/IP 模型是由 OSI 模型演化而来,TCP/IP 模型将 OSI 模型由七层简化为五层(一开始为四层),应用层、表示层、会话层统一为应用层。\n\n\n\n![image-20210913094813894](https://gitee.com/ajream/images/raw/master/img/20210913094816_image-20210913094813894.png)\n\n\n\n### 协议\n\nTCP/IP协议被称为传输控制协议/互联网协议,又称网络通讯协议(Transmission Control Protocol)。是由网络层的IP协议和传输层的TCP协议组成,是一个很大的协议集合。\n\n- 物理层和数据链路层没有定义任何特定协议,支持所有的标准和专用的协议。\n- 网络层定义了网络互联也就是IP协议,主要包括IP、ARP、RARP、ICMP、IGMP。\n- 传输层定义了TCP和UDP(User Datagram Protocol),我们会后面重点介绍一下TCP协议。\n- 应用层定义了HTTP(超文本传输协议)、FTP(文件传输协议)、DNS(域名系统)等协议。\n\n\n\n### 物理层\n\n计算机在传递数据的时候传递的都是0和1的数字,而物理层关心的是用什么信号来表示0和1,是否可以双向通信,最初的连接如何建立以及完成连接如何终止,总之,物理层是为数据传输提供可靠的环境。\n\n### 数据链路层\n\n数据链路层们于物理层和网络层之间,用来向网络层提供数据,就是把源计算机网络层传过来的信息传递给目标主机。\n数据链路层主要的作用包括:\n\n- 如何将数据组合成数据帧(Frame),帧是数据链路层的传输单位\n- 数据链路的建立、维护和拆除\n- 帧包装、帧传输、帧同步\n- 帧的差错恢复\n- 流量控制\n\n### 网络层\n\n网络层位于传输层和数据链路层之间,用于把数据从源主机经过若干个中间节点传送到目标主机,并向传输层提供最基础的数据传输服务,它要提供路由和选址的工作。\n\n### 传输层\n\n传输层主要提供以下几个功能\n\n1. 提供应用程序接口,为网络应用程序提供网络访问的途径;\n\n2. 提供可以从多个应用层序接收消息的功能(多路复用),同时也提供可以把消息分发给应用程序的功能(多路分解)。\n\n3. 对数据进行错误检测、流量控制。\n\n\n\n### 应用层\n\nTCP/IP的应用层对应于OSI的应用层、会话层、网络层,它们分别功能如下\n\n应用层:为用户的应用提供服务并支持网络访问。\n\n会话层:负责管理网络中计算之间的通信,提供传输层不具备的连接相关功能。\n\n表示层:负责转化数据格式,并处理数据加密和数据压缩。\n\n\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20201218111758350.png)","slug":"网络通信/网络协议","published":1,"updated":"2021-09-13T01:54:14.362Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rh008hakveh2yhheff","content":"

组成要素

    \n
  1. 语义: 语义是解释控制信息每个部分的意义。它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应

    \n
  2. \n
  3. 语法: 语法是用户数据与控制信息的结构与格式,以及数据出现的顺序

    \n
  4. \n
  5. 时序: 时序是对事件发生顺序的详细说明。(也可称为“同步”)

    \n
  6. \n
\n

人们形象地把这三个要素描述为:语义表示要做什么,语法表示要怎么做,时序表示做的顺序。

\n
\n

OSI参考模型

OSI七层模型(Open System Interconnect)即开放系统互连参考模型,是由ISO(International Organization for Standardization)国际标准化组织提出的,用于计算机或通信系统间互联的标准体系。

\n

OSI模型从上到下可分为七层,每一层都完成特定的功能,并为上一层提供服务,并使用下层所提供的服务。

\n

应用层:协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP

\n

表示层:数据的表示、安全、压缩。格式有,JPEG、ASCll、DECOIC、加密格式等

\n

会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话

\n

传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层

\n

网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP

\n

数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。

\n

物理层:建立、维护、断开物理连接。

\n

\"OSImodel\"

\n

数据传输过程

\n

\"img\"

\n

TCP-IP模型

TCP/IP 模型是由 OSI 模型演化而来,TCP/IP 模型将 OSI 模型由七层简化为五层(一开始为四层),应用层、表示层、会话层统一为应用层。

\n

\"image-20210913094813894\"

\n

协议

TCP/IP协议被称为传输控制协议/互联网协议,又称网络通讯协议(Transmission Control Protocol)。是由网络层的IP协议和传输层的TCP协议组成,是一个很大的协议集合。

\n
    \n
  • 物理层和数据链路层没有定义任何特定协议,支持所有的标准和专用的协议。
  • \n
  • 网络层定义了网络互联也就是IP协议,主要包括IP、ARP、RARP、ICMP、IGMP。
  • \n
  • 传输层定义了TCP和UDP(User Datagram Protocol),我们会后面重点介绍一下TCP协议。
  • \n
  • 应用层定义了HTTP(超文本传输协议)、FTP(文件传输协议)、DNS(域名系统)等协议。
  • \n
\n

物理层

计算机在传递数据的时候传递的都是0和1的数字,而物理层关心的是用什么信号来表示0和1,是否可以双向通信,最初的连接如何建立以及完成连接如何终止,总之,物理层是为数据传输提供可靠的环境。

\n

数据链路层

数据链路层们于物理层和网络层之间,用来向网络层提供数据,就是把源计算机网络层传过来的信息传递给目标主机。
数据链路层主要的作用包括:

\n
    \n
  • 如何将数据组合成数据帧(Frame),帧是数据链路层的传输单位
  • \n
  • 数据链路的建立、维护和拆除
  • \n
  • 帧包装、帧传输、帧同步
  • \n
  • 帧的差错恢复
  • \n
  • 流量控制
  • \n
\n

网络层

网络层位于传输层和数据链路层之间,用于把数据从源主机经过若干个中间节点传送到目标主机,并向传输层提供最基础的数据传输服务,它要提供路由和选址的工作。

\n

传输层

传输层主要提供以下几个功能

\n
    \n
  1. 提供应用程序接口,为网络应用程序提供网络访问的途径;

    \n
  2. \n
  3. 提供可以从多个应用层序接收消息的功能(多路复用),同时也提供可以把消息分发给应用程序的功能(多路分解)。

    \n
  4. \n
  5. 对数据进行错误检测、流量控制。

    \n
  6. \n
\n

应用层

TCP/IP的应用层对应于OSI的应用层、会话层、网络层,它们分别功能如下

\n

应用层:为用户的应用提供服务并支持网络访问。

\n

会话层:负责管理网络中计算之间的通信,提供传输层不具备的连接相关功能。

\n

表示层:负责转化数据格式,并处理数据加密和数据压缩。

\n

\"在这里插入图片描述\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

组成要素

    \n
  1. 语义: 语义是解释控制信息每个部分的意义。它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应

    \n
  2. \n
  3. 语法: 语法是用户数据与控制信息的结构与格式,以及数据出现的顺序

    \n
  4. \n
  5. 时序: 时序是对事件发生顺序的详细说明。(也可称为“同步”)

    \n
  6. \n
\n

人们形象地把这三个要素描述为:语义表示要做什么,语法表示要怎么做,时序表示做的顺序。

\n
\n

OSI参考模型

OSI七层模型(Open System Interconnect)即开放系统互连参考模型,是由ISO(International Organization for Standardization)国际标准化组织提出的,用于计算机或通信系统间互联的标准体系。

\n

OSI模型从上到下可分为七层,每一层都完成特定的功能,并为上一层提供服务,并使用下层所提供的服务。

\n

应用层:协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP

\n

表示层:数据的表示、安全、压缩。格式有,JPEG、ASCll、DECOIC、加密格式等

\n

会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话

\n

传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层

\n

网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP

\n

数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。

\n

物理层:建立、维护、断开物理连接。

\n

\"OSImodel\"

\n

数据传输过程

\n

\"img\"

\n

TCP-IP模型

TCP/IP 模型是由 OSI 模型演化而来,TCP/IP 模型将 OSI 模型由七层简化为五层(一开始为四层),应用层、表示层、会话层统一为应用层。

\n

\"image-20210913094813894\"

\n

协议

TCP/IP协议被称为传输控制协议/互联网协议,又称网络通讯协议(Transmission Control Protocol)。是由网络层的IP协议和传输层的TCP协议组成,是一个很大的协议集合。

\n
    \n
  • 物理层和数据链路层没有定义任何特定协议,支持所有的标准和专用的协议。
  • \n
  • 网络层定义了网络互联也就是IP协议,主要包括IP、ARP、RARP、ICMP、IGMP。
  • \n
  • 传输层定义了TCP和UDP(User Datagram Protocol),我们会后面重点介绍一下TCP协议。
  • \n
  • 应用层定义了HTTP(超文本传输协议)、FTP(文件传输协议)、DNS(域名系统)等协议。
  • \n
\n

物理层

计算机在传递数据的时候传递的都是0和1的数字,而物理层关心的是用什么信号来表示0和1,是否可以双向通信,最初的连接如何建立以及完成连接如何终止,总之,物理层是为数据传输提供可靠的环境。

\n

数据链路层

数据链路层们于物理层和网络层之间,用来向网络层提供数据,就是把源计算机网络层传过来的信息传递给目标主机。
数据链路层主要的作用包括:

\n
    \n
  • 如何将数据组合成数据帧(Frame),帧是数据链路层的传输单位
  • \n
  • 数据链路的建立、维护和拆除
  • \n
  • 帧包装、帧传输、帧同步
  • \n
  • 帧的差错恢复
  • \n
  • 流量控制
  • \n
\n

网络层

网络层位于传输层和数据链路层之间,用于把数据从源主机经过若干个中间节点传送到目标主机,并向传输层提供最基础的数据传输服务,它要提供路由和选址的工作。

\n

传输层

传输层主要提供以下几个功能

\n
    \n
  1. 提供应用程序接口,为网络应用程序提供网络访问的途径;

    \n
  2. \n
  3. 提供可以从多个应用层序接收消息的功能(多路复用),同时也提供可以把消息分发给应用程序的功能(多路分解)。

    \n
  4. \n
  5. 对数据进行错误检测、流量控制。

    \n
  6. \n
\n

应用层

TCP/IP的应用层对应于OSI的应用层、会话层、网络层,它们分别功能如下

\n

应用层:为用户的应用提供服务并支持网络访问。

\n

会话层:负责管理网络中计算之间的通信,提供传输层不具备的连接相关功能。

\n

表示层:负责转化数据格式,并处理数据加密和数据压缩。

\n

\"在这里插入图片描述\"

\n"},{"title":"计算机网络的产生","description":"计算机网络的产生与发展过程、计算机网络组成与结构、计算机网络性能指标","cover":"https://gitee.com/ajream/images/raw/master/img/20210913084452_c-s.png","abbrlink":"7dc1e032","date":"2021-09-12T00:25:15.000Z","katex":true,"_content":"\n\n## 计算机网络的产生\n\n### 分组交换网的出现\n\n分组交换技术是在**1960年代末**出现的,当时美国高级研究计划局(简称ARPA)为实现远程计算机之间的信息交换,资助建设一个试验性的网络,该网络被称为ARPANET。 ARPANET的主要研究成果之一就是开发一种新的网络协议 ,在 ARPANET 网络上对话必须使用这种网络协议。\n\n### 局域网、城域网、广域网\n\n#### 局域网\n\n1、局域网定义\n\n局域网(Local Area Network),简称LAN,是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。\n\n2、局域网介绍\n\n局域网(Local Area Network,LAN)是在一个局部的地理范围内(如一个学校、工厂和机关内),一般是方圆几千米以内,将各种计算机,外部设备和数据库等互相联接起来组成的计算机通信网。它可以通过数据通信网或专用数据电路,与远方的局域网、数据库或处理中心相连接,构成一个较大范围的信息处理系统。局域网可以实现文件管理、应用软件共享、打印机共享、扫描仪共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网严格意义上是封闭型的。它可以由办公室内几台甚至上千上万台计算机组成。决定局域网的主要技术要素为:网络拓扑,传输介质与介质访问控制方法。\n\n3、局域网组成\n\n局域网由硬件(包括服务器、工作站、打印机、网卡、互联设备等)和网络传输介质(光纤和WIFI等),以及软件所组成。\n\n4、局域网拓扑结构\n\n星形、树形、总线形和环形。\n\n5、局域网四种技术类型/标准\n\n以太网(Ethernet);令牌环(Token Ring);令牌总线(Token Bus),如ARCNET;光纤分布式数据接口(FDDI)。目前,以太网(Ethernet)类型的局域网应用最为广泛。\n\n#### 城域网\n\n1、城域网定义\n\n城域网(Metropolitan Area Network),简称MAN,是在一个城市范围内所建立的计算机通信网。属宽带局域网。由于采用具有有源交换元件的局域网技术,网中传输时延较小,它的传输媒介主要采用光缆,传输速率在100兆比特/秒以上。\n\n2、城域网介绍\n\n城域网MAN是基于一种大型的局域网LAN,通常使用与局域网LAN相似的技术。将城域网单独列出来的一个主要原因是它有自己的一个标准:分布式队列双总线DQDB(Distributed Queue Dual Bus),即IEEE802.6。DQDB是由双总线构成,所有的计算机都连结在上面。\n\nMAN的一个重要用途是用作骨干网,通过它将位于同一城市内不同地点的主机、数据库,以及LAN等互相联接起来,这与WAN的作用有相似之处,但两者在实现方法与性能上有很大差别。\n\n建设局域网或广域网包括资源子网和通信子网两个方面,而城域网的建设主要集中在通信子网上,其中也包含两个方面:\n\n- 一是城市骨干网,它与中国的骨干网相连。\n\n- 二是城市接入网,它把本地所有的联网用户与城市骨干网相连。\n\n#### 广域网\n\n广域网(Wide Area Network),简称 WAN,又称广域网、外网、公网(相对应,局域网LAN也称内网、私网)。是连接不同地区[局域网](https://baike.baidu.com/item/局域网)或[城域网](https://baike.baidu.com/item/城域网)计算机通信的远程网。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。广域网并不等同于[互联网](https://baike.baidu.com/item/互联网)。\n\n广域网是网络专业的一个专业术语,通常特指跨接很大物理范围的[计算机网络](https://www.baidu.com/s?wd=计算机网络&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao),一般要超过几十公里,比如某公司总部在北京,分公司在上海,这两个地方的分别有各自的局域网,这两个局域网通过自建或者租用通讯线路的方式连接起来就构成了一个广域网络。互联网也可以说是广域网的一个实例\n\n\n\n### 计算机体系结构形成\n\n第一阶段:从单个网络(分组交换网ARPANET)->互联网\n\n第二阶段:三级结构因特网的形成【国家主干网->地区网->校园网】\n\n第三阶段:多级结构因特网\n\n\n{% note modern %}\n\nISP: 因特网服务供应商\n\nNAP: 网络接入点\n\n{% endnote %}\n\n\n\n\n\n\n\n## 计算机网络组成与结构\n\n\n\n结构与功能密不可分,计算机网络具有两大功能:数据处理与数据通信,因此计算机组成结构可以据此分为资源子网、通信子网\n\n资源子网:主计算机 + 终端\n\n通信子网:通信控制处理机 + 通信线路\n\n\n\n## 计算机网络性能指标\n\n\n\n### 带宽\n\n带宽一词最初指的是电磁波频带的宽度,也就是信号的最高频率与最低频率的差值。目前,它被更广泛地借用在数字通信中,用来描述网络或线路理论上传输数据的最高速率。\n\n常用带宽单位:bit/s(bps)、kb/s(kbps)、Mb/s(=$10^6$ b/s)、 Gb/s(=$10^9$ b/s)、Tb/s(=$10^{12}$ b/s)……生活中为了方便常常省略后面的“b/s”,比如“线路带宽是10M”,实际指的是 10Mb/s.\n\n\n\n### 时延\n\n\n\n时延是指一个报文或分组从一个网络的一端传送到另一个端所需要的时间。它包括了发送时延,传播时延,处理时延。(时延=发送时延+传播时延+处理时延)一般,发送时延与传播时延是我们主要考虑的。对于报文长度较大的情况,发送时延是主要矛盾;报文长度较小的情况,传播时延是主要矛盾。\n\n\n\n发送时延:指结点在发送数据时使数据块从结点进入到传输媒体所需的时间,也就是从数据块的第一个bit开始发送算起,到最后一个bit发送完毕所需的时间。\n\n发送时延=数据帧长度/发送速率\n\n\n\n传播时延:指电磁信号或光信号在传输介质中传播一定的距离所花费的时间,即从发送端发送数据开始,到接收端收到数据(或者从接收端发送确认帧,到发送端收到确认帧),总共经历的时间。\n\n\n\n处理时延:指数据在交换结点为了存储转发而进行一些必要的处理所花费的时间,在结点缓存队列中,分组排队所经历的时延是处理时延的重要组成部分。\n\n\n\n\n\n\n\n","source":"_posts/网络通信/计算机网络的产生.md","raw":"---\ntitle: 计算机网络的产生\ntags:\n - 网络通信\ncategories:\n - 网络通信\ndescription: 计算机网络的产生与发展过程、计算机网络组成与结构、计算机网络性能指标\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210913084452_c-s.png'\nabbrlink: 7dc1e032\ndate: 2021-09-12 08:25:15\nkatex: true\n---\n\n\n## 计算机网络的产生\n\n### 分组交换网的出现\n\n分组交换技术是在**1960年代末**出现的,当时美国高级研究计划局(简称ARPA)为实现远程计算机之间的信息交换,资助建设一个试验性的网络,该网络被称为ARPANET。 ARPANET的主要研究成果之一就是开发一种新的网络协议 ,在 ARPANET 网络上对话必须使用这种网络协议。\n\n### 局域网、城域网、广域网\n\n#### 局域网\n\n1、局域网定义\n\n局域网(Local Area Network),简称LAN,是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。\n\n2、局域网介绍\n\n局域网(Local Area Network,LAN)是在一个局部的地理范围内(如一个学校、工厂和机关内),一般是方圆几千米以内,将各种计算机,外部设备和数据库等互相联接起来组成的计算机通信网。它可以通过数据通信网或专用数据电路,与远方的局域网、数据库或处理中心相连接,构成一个较大范围的信息处理系统。局域网可以实现文件管理、应用软件共享、打印机共享、扫描仪共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网严格意义上是封闭型的。它可以由办公室内几台甚至上千上万台计算机组成。决定局域网的主要技术要素为:网络拓扑,传输介质与介质访问控制方法。\n\n3、局域网组成\n\n局域网由硬件(包括服务器、工作站、打印机、网卡、互联设备等)和网络传输介质(光纤和WIFI等),以及软件所组成。\n\n4、局域网拓扑结构\n\n星形、树形、总线形和环形。\n\n5、局域网四种技术类型/标准\n\n以太网(Ethernet);令牌环(Token Ring);令牌总线(Token Bus),如ARCNET;光纤分布式数据接口(FDDI)。目前,以太网(Ethernet)类型的局域网应用最为广泛。\n\n#### 城域网\n\n1、城域网定义\n\n城域网(Metropolitan Area Network),简称MAN,是在一个城市范围内所建立的计算机通信网。属宽带局域网。由于采用具有有源交换元件的局域网技术,网中传输时延较小,它的传输媒介主要采用光缆,传输速率在100兆比特/秒以上。\n\n2、城域网介绍\n\n城域网MAN是基于一种大型的局域网LAN,通常使用与局域网LAN相似的技术。将城域网单独列出来的一个主要原因是它有自己的一个标准:分布式队列双总线DQDB(Distributed Queue Dual Bus),即IEEE802.6。DQDB是由双总线构成,所有的计算机都连结在上面。\n\nMAN的一个重要用途是用作骨干网,通过它将位于同一城市内不同地点的主机、数据库,以及LAN等互相联接起来,这与WAN的作用有相似之处,但两者在实现方法与性能上有很大差别。\n\n建设局域网或广域网包括资源子网和通信子网两个方面,而城域网的建设主要集中在通信子网上,其中也包含两个方面:\n\n- 一是城市骨干网,它与中国的骨干网相连。\n\n- 二是城市接入网,它把本地所有的联网用户与城市骨干网相连。\n\n#### 广域网\n\n广域网(Wide Area Network),简称 WAN,又称广域网、外网、公网(相对应,局域网LAN也称内网、私网)。是连接不同地区[局域网](https://baike.baidu.com/item/局域网)或[城域网](https://baike.baidu.com/item/城域网)计算机通信的远程网。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。广域网并不等同于[互联网](https://baike.baidu.com/item/互联网)。\n\n广域网是网络专业的一个专业术语,通常特指跨接很大物理范围的[计算机网络](https://www.baidu.com/s?wd=计算机网络&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao),一般要超过几十公里,比如某公司总部在北京,分公司在上海,这两个地方的分别有各自的局域网,这两个局域网通过自建或者租用通讯线路的方式连接起来就构成了一个广域网络。互联网也可以说是广域网的一个实例\n\n\n\n### 计算机体系结构形成\n\n第一阶段:从单个网络(分组交换网ARPANET)->互联网\n\n第二阶段:三级结构因特网的形成【国家主干网->地区网->校园网】\n\n第三阶段:多级结构因特网\n\n\n{% note modern %}\n\nISP: 因特网服务供应商\n\nNAP: 网络接入点\n\n{% endnote %}\n\n\n\n\n\n\n\n## 计算机网络组成与结构\n\n\n\n结构与功能密不可分,计算机网络具有两大功能:数据处理与数据通信,因此计算机组成结构可以据此分为资源子网、通信子网\n\n资源子网:主计算机 + 终端\n\n通信子网:通信控制处理机 + 通信线路\n\n\n\n## 计算机网络性能指标\n\n\n\n### 带宽\n\n带宽一词最初指的是电磁波频带的宽度,也就是信号的最高频率与最低频率的差值。目前,它被更广泛地借用在数字通信中,用来描述网络或线路理论上传输数据的最高速率。\n\n常用带宽单位:bit/s(bps)、kb/s(kbps)、Mb/s(=$10^6$ b/s)、 Gb/s(=$10^9$ b/s)、Tb/s(=$10^{12}$ b/s)……生活中为了方便常常省略后面的“b/s”,比如“线路带宽是10M”,实际指的是 10Mb/s.\n\n\n\n### 时延\n\n\n\n时延是指一个报文或分组从一个网络的一端传送到另一个端所需要的时间。它包括了发送时延,传播时延,处理时延。(时延=发送时延+传播时延+处理时延)一般,发送时延与传播时延是我们主要考虑的。对于报文长度较大的情况,发送时延是主要矛盾;报文长度较小的情况,传播时延是主要矛盾。\n\n\n\n发送时延:指结点在发送数据时使数据块从结点进入到传输媒体所需的时间,也就是从数据块的第一个bit开始发送算起,到最后一个bit发送完毕所需的时间。\n\n发送时延=数据帧长度/发送速率\n\n\n\n传播时延:指电磁信号或光信号在传输介质中传播一定的距离所花费的时间,即从发送端发送数据开始,到接收端收到数据(或者从接收端发送确认帧,到发送端收到确认帧),总共经历的时间。\n\n\n\n处理时延:指数据在交换结点为了存储转发而进行一些必要的处理所花费的时间,在结点缓存队列中,分组排队所经历的时延是处理时延的重要组成部分。\n\n\n\n\n\n\n\n","slug":"网络通信/计算机网络的产生","published":1,"updated":"2021-09-13T02:00:10.634Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ri008kakve8zmh4jwv","content":"

计算机网络的产生

分组交换网的出现

分组交换技术是在1960年代末出现的,当时美国高级研究计划局(简称ARPA)为实现远程计算机之间的信息交换,资助建设一个试验性的网络,该网络被称为ARPANET。 ARPANET的主要研究成果之一就是开发一种新的网络协议 ,在 ARPANET 网络上对话必须使用这种网络协议。

\n

局域网、城域网、广域网

局域网

1、局域网定义

\n

局域网(Local Area Network),简称LAN,是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。

\n

2、局域网介绍

\n

局域网(Local Area Network,LAN)是在一个局部的地理范围内(如一个学校、工厂和机关内),一般是方圆几千米以内,将各种计算机,外部设备和数据库等互相联接起来组成的计算机通信网。它可以通过数据通信网或专用数据电路,与远方的局域网、数据库或处理中心相连接,构成一个较大范围的信息处理系统。局域网可以实现文件管理、应用软件共享、打印机共享、扫描仪共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网严格意义上是封闭型的。它可以由办公室内几台甚至上千上万台计算机组成。决定局域网的主要技术要素为:网络拓扑,传输介质与介质访问控制方法。

\n

3、局域网组成

\n

局域网由硬件(包括服务器、工作站、打印机、网卡、互联设备等)和网络传输介质(光纤和WIFI等),以及软件所组成。

\n

4、局域网拓扑结构

\n

星形、树形、总线形和环形。

\n

5、局域网四种技术类型/标准

\n

以太网(Ethernet);令牌环(Token Ring);令牌总线(Token Bus),如ARCNET;光纤分布式数据接口(FDDI)。目前,以太网(Ethernet)类型的局域网应用最为广泛。

\n

城域网

1、城域网定义

\n

城域网(Metropolitan Area Network),简称MAN,是在一个城市范围内所建立的计算机通信网。属宽带局域网。由于采用具有有源交换元件的局域网技术,网中传输时延较小,它的传输媒介主要采用光缆,传输速率在100兆比特/秒以上。

\n

2、城域网介绍

\n

城域网MAN是基于一种大型的局域网LAN,通常使用与局域网LAN相似的技术。将城域网单独列出来的一个主要原因是它有自己的一个标准:分布式队列双总线DQDB(Distributed Queue Dual Bus),即IEEE802.6。DQDB是由双总线构成,所有的计算机都连结在上面。

\n

MAN的一个重要用途是用作骨干网,通过它将位于同一城市内不同地点的主机、数据库,以及LAN等互相联接起来,这与WAN的作用有相似之处,但两者在实现方法与性能上有很大差别。

\n

建设局域网或广域网包括资源子网和通信子网两个方面,而城域网的建设主要集中在通信子网上,其中也包含两个方面:

\n
    \n
  • 一是城市骨干网,它与中国的骨干网相连。

    \n
  • \n
  • 二是城市接入网,它把本地所有的联网用户与城市骨干网相连。

    \n
  • \n
\n

广域网

广域网(Wide Area Network),简称 WAN,又称广域网、外网、公网(相对应,局域网LAN也称内网、私网)。是连接不同地区局域网城域网计算机通信的远程网。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。广域网并不等同于互联网

\n

广域网是网络专业的一个专业术语,通常特指跨接很大物理范围的计算机网络,一般要超过几十公里,比如某公司总部在北京,分公司在上海,这两个地方的分别有各自的局域网,这两个局域网通过自建或者租用通讯线路的方式连接起来就构成了一个广域网络。互联网也可以说是广域网的一个实例

\n

计算机体系结构形成

第一阶段:从单个网络(分组交换网ARPANET)->互联网

\n

第二阶段:三级结构因特网的形成【国家主干网->地区网->校园网】

\n

第三阶段:多级结构因特网

\n

ISP: 因特网服务供应商

\n

NAP: 网络接入点

\n
\n

计算机网络组成与结构

结构与功能密不可分,计算机网络具有两大功能:数据处理与数据通信,因此计算机组成结构可以据此分为资源子网、通信子网

\n

资源子网:主计算机 + 终端

\n

通信子网:通信控制处理机 + 通信线路

\n

计算机网络性能指标

带宽

带宽一词最初指的是电磁波频带的宽度,也就是信号的最高频率与最低频率的差值。目前,它被更广泛地借用在数字通信中,用来描述网络或线路理论上传输数据的最高速率。

\n

常用带宽单位:bit/s(bps)、kb/s(kbps)、Mb/s(=$10^6$ b/s)、 Gb/s(=$10^9$ b/s)、Tb/s(=$10^{12}$ b/s)……生活中为了方便常常省略后面的“b/s”,比如“线路带宽是10M”,实际指的是 10Mb/s.

\n

时延

时延是指一个报文或分组从一个网络的一端传送到另一个端所需要的时间。它包括了发送时延,传播时延,处理时延。(时延=发送时延+传播时延+处理时延)一般,发送时延与传播时延是我们主要考虑的。对于报文长度较大的情况,发送时延是主要矛盾;报文长度较小的情况,传播时延是主要矛盾。

\n

发送时延:指结点在发送数据时使数据块从结点进入到传输媒体所需的时间,也就是从数据块的第一个bit开始发送算起,到最后一个bit发送完毕所需的时间。

\n

发送时延=数据帧长度/发送速率

\n

传播时延:指电磁信号或光信号在传输介质中传播一定的距离所花费的时间,即从发送端发送数据开始,到接收端收到数据(或者从接收端发送确认帧,到发送端收到确认帧),总共经历的时间。

\n

处理时延:指数据在交换结点为了存储转发而进行一些必要的处理所花费的时间,在结点缓存队列中,分组排队所经历的时延是处理时延的重要组成部分。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

计算机网络的产生

分组交换网的出现

分组交换技术是在1960年代末出现的,当时美国高级研究计划局(简称ARPA)为实现远程计算机之间的信息交换,资助建设一个试验性的网络,该网络被称为ARPANET。 ARPANET的主要研究成果之一就是开发一种新的网络协议 ,在 ARPANET 网络上对话必须使用这种网络协议。

\n

局域网、城域网、广域网

局域网

1、局域网定义

\n

局域网(Local Area Network),简称LAN,是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。

\n

2、局域网介绍

\n

局域网(Local Area Network,LAN)是在一个局部的地理范围内(如一个学校、工厂和机关内),一般是方圆几千米以内,将各种计算机,外部设备和数据库等互相联接起来组成的计算机通信网。它可以通过数据通信网或专用数据电路,与远方的局域网、数据库或处理中心相连接,构成一个较大范围的信息处理系统。局域网可以实现文件管理、应用软件共享、打印机共享、扫描仪共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网严格意义上是封闭型的。它可以由办公室内几台甚至上千上万台计算机组成。决定局域网的主要技术要素为:网络拓扑,传输介质与介质访问控制方法。

\n

3、局域网组成

\n

局域网由硬件(包括服务器、工作站、打印机、网卡、互联设备等)和网络传输介质(光纤和WIFI等),以及软件所组成。

\n

4、局域网拓扑结构

\n

星形、树形、总线形和环形。

\n

5、局域网四种技术类型/标准

\n

以太网(Ethernet);令牌环(Token Ring);令牌总线(Token Bus),如ARCNET;光纤分布式数据接口(FDDI)。目前,以太网(Ethernet)类型的局域网应用最为广泛。

\n

城域网

1、城域网定义

\n

城域网(Metropolitan Area Network),简称MAN,是在一个城市范围内所建立的计算机通信网。属宽带局域网。由于采用具有有源交换元件的局域网技术,网中传输时延较小,它的传输媒介主要采用光缆,传输速率在100兆比特/秒以上。

\n

2、城域网介绍

\n

城域网MAN是基于一种大型的局域网LAN,通常使用与局域网LAN相似的技术。将城域网单独列出来的一个主要原因是它有自己的一个标准:分布式队列双总线DQDB(Distributed Queue Dual Bus),即IEEE802.6。DQDB是由双总线构成,所有的计算机都连结在上面。

\n

MAN的一个重要用途是用作骨干网,通过它将位于同一城市内不同地点的主机、数据库,以及LAN等互相联接起来,这与WAN的作用有相似之处,但两者在实现方法与性能上有很大差别。

\n

建设局域网或广域网包括资源子网和通信子网两个方面,而城域网的建设主要集中在通信子网上,其中也包含两个方面:

\n
    \n
  • 一是城市骨干网,它与中国的骨干网相连。

    \n
  • \n
  • 二是城市接入网,它把本地所有的联网用户与城市骨干网相连。

    \n
  • \n
\n

广域网

广域网(Wide Area Network),简称 WAN,又称广域网、外网、公网(相对应,局域网LAN也称内网、私网)。是连接不同地区局域网城域网计算机通信的远程网。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。广域网并不等同于互联网

\n

广域网是网络专业的一个专业术语,通常特指跨接很大物理范围的计算机网络,一般要超过几十公里,比如某公司总部在北京,分公司在上海,这两个地方的分别有各自的局域网,这两个局域网通过自建或者租用通讯线路的方式连接起来就构成了一个广域网络。互联网也可以说是广域网的一个实例

\n

计算机体系结构形成

第一阶段:从单个网络(分组交换网ARPANET)->互联网

\n

第二阶段:三级结构因特网的形成【国家主干网->地区网->校园网】

\n

第三阶段:多级结构因特网

\n

ISP: 因特网服务供应商

\n

NAP: 网络接入点

\n
\n

计算机网络组成与结构

结构与功能密不可分,计算机网络具有两大功能:数据处理与数据通信,因此计算机组成结构可以据此分为资源子网、通信子网

\n

资源子网:主计算机 + 终端

\n

通信子网:通信控制处理机 + 通信线路

\n

计算机网络性能指标

带宽

带宽一词最初指的是电磁波频带的宽度,也就是信号的最高频率与最低频率的差值。目前,它被更广泛地借用在数字通信中,用来描述网络或线路理论上传输数据的最高速率。

\n

常用带宽单位:bit/s(bps)、kb/s(kbps)、Mb/s(=$10^6$ b/s)、 Gb/s(=$10^9$ b/s)、Tb/s(=$10^{12}$ b/s)……生活中为了方便常常省略后面的“b/s”,比如“线路带宽是10M”,实际指的是 10Mb/s.

\n

时延

时延是指一个报文或分组从一个网络的一端传送到另一个端所需要的时间。它包括了发送时延,传播时延,处理时延。(时延=发送时延+传播时延+处理时延)一般,发送时延与传播时延是我们主要考虑的。对于报文长度较大的情况,发送时延是主要矛盾;报文长度较小的情况,传播时延是主要矛盾。

\n

发送时延:指结点在发送数据时使数据块从结点进入到传输媒体所需的时间,也就是从数据块的第一个bit开始发送算起,到最后一个bit发送完毕所需的时间。

\n

发送时延=数据帧长度/发送速率

\n

传播时延:指电磁信号或光信号在传输介质中传播一定的距离所花费的时间,即从发送端发送数据开始,到接收端收到数据(或者从接收端发送确认帧,到发送端收到确认帧),总共经历的时间。

\n

处理时延:指数据在交换结点为了存储转发而进行一些必要的处理所花费的时间,在结点缓存队列中,分组排队所经历的时延是处理时延的重要组成部分。

\n"},{"title":"古典加密算法","date":"2021-09-14T15:13:59.000Z","katex":true,"abbrlink":"ee4b2b4d","description":"古典加密算法了解,置换加密,替代加密","cover":"https://gitee.com/ajream/images/raw/master/img/20210914232714_web-save.png","_content":"\n## 置换加密\n\n**置换密码算法**的原理是不改变明文字符,只将字符在明文中的排列顺序改变,从而实现明文信息的加密。置换密码有时又称为换位密码。\n**矩阵换位法**是实现置换密码的一种常用方法。它将明文中的字母按照给的顺序安排在一个矩阵中,然后用根据密钥提供的顺序重新组合矩阵中字母,从而形成密文。\n\n\n## 替代加密\n\n替代密码算法的原理是使用替代法进行加密,就是将明文中的字符用其它字符替代后形成密文。例如:明文字母 a,b,c,d ,用 D,E,F,G做对应替换后形成密文。 \n\n替代密码包括多种类型,如单表替代密码,多明码替代密码,多字母替代密码,多表替代密码等。下面我们介绍一种典型的单表替代密码,恺撒(caesar)密码,又叫循环移位密码。它的加密方法就是将明文中的每个字母用此字符在字母表中后面第 k 个字母替代。\n\n它的加密过程可以表示为下面的函数:\n\nE(m) = (m+k) mod n\n\n其中:\n- m 为明文字母在字母表中的位置数; \n- n 为字母表中的字母个数; \n- k 为密钥;\n- E(m) 为密文字母在字母表中对应的位置数。\n\n例如,对于明文字母 H,其在字母表中的位置数为 8,设 k=4,则按照上式计算出 来的密文为 L:\n\nE(8) = (m+k) mod n = (8+4) mod 26 = 12 = L","source":"_posts/网络安全/古典加密算法.md","raw":"---\ntitle: 古典加密算法\ndate: '2021-09-14 23:13:59'\nkatex: true\nabbrlink: ee4b2b4d\ntags:\n - 网络安全\ncategories:\n - 网络安全\ndescription: 古典加密算法了解,置换加密,替代加密\ncover: https://gitee.com/ajream/images/raw/master/img/20210914232714_web-save.png\n---\n\n## 置换加密\n\n**置换密码算法**的原理是不改变明文字符,只将字符在明文中的排列顺序改变,从而实现明文信息的加密。置换密码有时又称为换位密码。\n**矩阵换位法**是实现置换密码的一种常用方法。它将明文中的字母按照给的顺序安排在一个矩阵中,然后用根据密钥提供的顺序重新组合矩阵中字母,从而形成密文。\n\n\n## 替代加密\n\n替代密码算法的原理是使用替代法进行加密,就是将明文中的字符用其它字符替代后形成密文。例如:明文字母 a,b,c,d ,用 D,E,F,G做对应替换后形成密文。 \n\n替代密码包括多种类型,如单表替代密码,多明码替代密码,多字母替代密码,多表替代密码等。下面我们介绍一种典型的单表替代密码,恺撒(caesar)密码,又叫循环移位密码。它的加密方法就是将明文中的每个字母用此字符在字母表中后面第 k 个字母替代。\n\n它的加密过程可以表示为下面的函数:\n\nE(m) = (m+k) mod n\n\n其中:\n- m 为明文字母在字母表中的位置数; \n- n 为字母表中的字母个数; \n- k 为密钥;\n- E(m) 为密文字母在字母表中对应的位置数。\n\n例如,对于明文字母 H,其在字母表中的位置数为 8,设 k=4,则按照上式计算出 来的密文为 L:\n\nE(8) = (m+k) mod n = (8+4) mod 26 = 12 = L","slug":"网络安全/古典加密算法","published":1,"updated":"2021-09-16T14:31:54.376Z","_id":"cktk8o6rj008oakvef3va6zbp","comments":1,"layout":"post","photos":[],"link":"","content":"

置换加密

\n

置换密码算法的原理是不改变明文字符,只将字符在明文中的排列顺序改变,从而实现明文信息的加密。置换密码有时又称为换位密码。
\n矩阵换位法是实现置换密码的一种常用方法。它将明文中的字母按照给的顺序安排在一个矩阵中,然后用根据密钥提供的顺序重新组合矩阵中字母,从而形成密文。

\n

替代加密

\n

替代密码算法的原理是使用替代法进行加密,就是将明文中的字符用其它字符替代后形成密文。例如:明文字母 a,b,c,d ,用 D,E,F,G做对应替换后形成密文。

\n

替代密码包括多种类型,如单表替代密码,多明码替代密码,多字母替代密码,多表替代密码等。下面我们介绍一种典型的单表替代密码,恺撒(caesar)密码,又叫循环移位密码。它的加密方法就是将明文中的每个字母用此字符在字母表中后面第 k 个字母替代。

\n

它的加密过程可以表示为下面的函数:

\n

E(m) = (m+k) mod n

\n

其中:

\n
    \n
  • m 为明文字母在字母表中的位置数;
  • \n
  • n 为字母表中的字母个数;
  • \n
  • k 为密钥;
  • \n
  • E(m) 为密文字母在字母表中对应的位置数。
  • \n
\n

例如,对于明文字母 H,其在字母表中的位置数为 8,设 k=4,则按照上式计算出 来的密文为 L:

\n

E(8) = (m+k) mod n = (8+4) mod 26 = 12 = L

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

置换加密

\n

置换密码算法的原理是不改变明文字符,只将字符在明文中的排列顺序改变,从而实现明文信息的加密。置换密码有时又称为换位密码。
\n矩阵换位法是实现置换密码的一种常用方法。它将明文中的字母按照给的顺序安排在一个矩阵中,然后用根据密钥提供的顺序重新组合矩阵中字母,从而形成密文。

\n

替代加密

\n

替代密码算法的原理是使用替代法进行加密,就是将明文中的字符用其它字符替代后形成密文。例如:明文字母 a,b,c,d ,用 D,E,F,G做对应替换后形成密文。

\n

替代密码包括多种类型,如单表替代密码,多明码替代密码,多字母替代密码,多表替代密码等。下面我们介绍一种典型的单表替代密码,恺撒(caesar)密码,又叫循环移位密码。它的加密方法就是将明文中的每个字母用此字符在字母表中后面第 k 个字母替代。

\n

它的加密过程可以表示为下面的函数:

\n

E(m) = (m+k) mod n

\n

其中:

\n
    \n
  • m 为明文字母在字母表中的位置数;
  • \n
  • n 为字母表中的字母个数;
  • \n
  • k 为密钥;
  • \n
  • E(m) 为密文字母在字母表中对应的位置数。
  • \n
\n

例如,对于明文字母 H,其在字母表中的位置数为 8,设 k=4,则按照上式计算出 来的密文为 L:

\n

E(8) = (m+k) mod n = (8+4) mod 26 = 12 = L

\n"},{"title":"51单片机学习笔记(五)","description":"逐次逼近式AD转换器原理介绍,PCF8591模块介绍","abbrlink":"c4d68191","date":"2021-08-19T11:02:48.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n\n\n\n\n## 逐次逼近式AD转换器\n\n\n\n这是一个基本原理图\n\n首先,内部产生一个参考电压Vref,然后与要进行测量的电压Vin进行比较,如果Vref<=Vin,就产生一个“1”信号(否则产生“0”信号),并存储到N位寄存器的最高位,然重新生成参考电压信号Vref = Vref+0.5Vref,(如果前面产生的是“0”信号,则新的参考电压Vref = 0.5Vref),再与Vin比较,将比较结果(“0”或“1”存到寄存器的下一位),循环往复,直到N位寄存器全部存满数据,最后通过锁存器输出转化后的数字信号。\n\n![image-20210824143628336](https://gitee.com/ajream/images/raw/master/img/20210824143631_image-20210824143628336.png)\n\n比如:Vin = 3.75V, Vref = 2.5V\n\n1. Vref=2.5 < 3.75,因此寄存器最高位存储数据1,然后重新生成Vref = 2.5+0.5*2.5 = 3.75\n2. 此时Vref=3.75 == Vin, 返回比较结果1, 存储下来, 然后重新生成Vref = 3.75+3.75*0.5 = 5.6\n3. 此时Vref=5.6 > Vin,返回比较结果0,存储下来。。。。。\n4. 最后结果是:`1100 0000`\n\n\n\n\n\n## PCF8591模块\n\n\n\n### 简介\n\nPCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入、一个输出和一个串行 I2C 总线接口;\n\n3个地址引脚A0、A1 和 A2 用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件;\n\nPCF8591由于其使用的简单方便和集成度高,在单片机应用系统中得到了广泛的应用。\n\n特点:\n\n1. 单电源供电\n\n2. 工作电压:2.5 V ~ 6 V\n\n3. I2C总线串行输入/输出\n\n4. 通过3个硬件地址引脚编址\n\n5. 采样速率取决于 I2C 总线传输速率决定\n\n6. 4个模拟输入可编程为单端或差分输入\n\n7. 自动增量通道选择\n\n8. 8位逐次比较型 A/D 转换\n\n\n\n管脚定义:\n\n![image-20210824145451094](https://gitee.com/ajream/images/raw/master/img/20210824145452_image-20210824145451094.png)\n\n1. AIN0~AIN3:模拟量输入通道\n2. AOUT:模拟输出通道\n3. A0~A2:硬件设备地址\n4. VDD:电源正极\n5. VSS:电源负极\n6. VREF:参考电压输入。\n7. EXT:振荡器输入时,内部/外部的切换开关。\n8. OSC:振荡器输入/输出。\n9. SCL:I2C BUS时钟输入。\n10. SDA:I2C BUS 数据输入/输出。\n11. AGND:模拟地,模拟信号和基准电源的参考地\n\n\n\n\n\n开发板中的PCF8591接线:\n\n![image-20210824145815414](https://gitee.com/ajream/images/raw/master/img/20210824145816_image-20210824145815414.png)\n\n\n\n\n\n### PCF8591地址\n\nI2C 总线系统中的每一片PCF8591 通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分\n\n可编程部分必须根据地址引脚A0、A1 和 A2 来设置,因此I2C系统中最多可接8个PCF8591\n\n在I2C 总线协议中地址必须是起始条件后作为第一个字节发送。\n\n地址字节的最后一位是用于设置以后数据传输方向的读/写位(1为读操作,0为写操作)\n\n\n\n![image-20210824150249772](https://gitee.com/ajream/images/raw/master/img/20210824150251_image-20210824150249772.png)\n\n\n\n\n\n### PCF8591控制字节\n\n发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。\n\n控制寄存器的高半字节用于允许模拟输出,以及可以将模拟输入编程为单端或差分输入。\n\n低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置1,每次A/D 转换后通道号将自动增加。\n\n\n\n\n\n\n\n\n\n## 例子\n\n### (一)\n\n利用I2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,通过数码管显示\n\n```c\n/*********************************************************************************\n* 【作 者】:\t清翔电子:向量\n* 【版 本】:\tV1.0\n* 【网 站】:\thttp://www.qxmcu.com/ \n* 【淘宝店铺】:\thttp://qxmcu.taobao.com/\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,数码管\n\t\t\t\t\t显示。\t\t \t\t\t \t\t\t \n* 【使用说明】: \t用手转动AD旁边的电位器头,数码管数值会随之变化。\n**********************************************************************************/\n#include \n#include \n\n#define PCF8591ADDR 0X90 //PCF8591地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n#define\tCH0 0\t\t\t //AD通道0\n#define\tCH1\t1\t\t\t //AD通道1\n#define\tCH2\t2\t\t\t //AD通道2\n#define\tCH3\t3\t\t\t //AD通道3\n#define\tDAout\t0x40\t //DA输出命令\n\n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n使用typedef给已有数据类型取别名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\ntypedef unsigned char u8;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\ntypedef unsigned int u16;\n\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1; //I2C时钟总线\nsbit SDA = P2^0; //I2C数据总线\nuint num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\n//数码管位选码 //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位 \nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数名\t:void delay(INT16U ms)\n参数\t:ms,毫秒延时形参\n返回值\t:无\n描述\t:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid delay(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n/*====================================\n函数\t:void delay5us()\n参数\t:无\n返回值\t:无\n描述\t:12T 51单片机5微秒延时函数自适应时钟\n====================================*/\nvoid delay5us()\n{\n\t#if MAIN_Fosc == 11059200\n\t\t_nop_();\n\t#elif MAIN_Fosc == 12000000\n\t\t_nop_();\n\t#elif MAIN_Fosc == 22118400\n\t\t_nop_(); _nop_(); _nop_();\n\t#endif\n}\n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示变量取值0-65535 \n返回值\t:无\n描述\t:数码管动态显示函数第一位显示小数点\n====================================*/\n//void display(uint i)\n//{\n//\tstatic uchar wei;\n//\t \t\t\n//\tP0 = 0XFF;//清除断码\n//\tWE = 1;//打开位选锁存器\n//\tP0 = SMGwei[wei];\n//\tWE = 0;//锁存位选数据\n//\n//\tswitch(wei)\n//\t{\n//\t\tcase 0: DU = 1; P0 = SMGduan[i / 1000] | 0x80; \tDU = 0; break;//千位显示小数点\n//\t\tcase 1: DU = 1; P0 = SMGduan[i % 1000 / 100]; \tDU = 0; break;//显示百位\n//\t\tcase 2: DU = 1; P0 = SMGduan[i % 100 / 10]; \tDU = 0; break;//显示十位\t\n//\t\tcase 3: DU = 1; P0 = SMGduan[i % 10]; \t\t\tDU = 0; break;//显示个位\t\t\n//\t}\n//\twei++;\n//\tif(wei == 4)\n//\t\twei = 0;\n//}\n\n/*====================================\n函数:display(INT16U Value)\n参数:Value,显示值 取值0-65535\n描述:共阴极数码管显示函数可显示一个字节的数\n====================================*/\nvoid display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型\n{\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\tP0 = SMGduan[Value/1000]|0x80;\t//数码管显示千位\n\tDU = 1;\t\t\t\t\t\t\t//打开段选\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tP0 = SMGwei[0];\t\t\t\t //第一位数码管\n\tWE = 1;\t\t\t\t\t\t//打开位选\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%1000/100]; //显示百位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[1];\t\t\t //第二位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%100/10];\t\t//显示十位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[2];\t\t\t\t//第三位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%10];\t\t//显示个位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[3];\t\t\t\t//第四位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n}\n\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart()\n{\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop()\n{\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK()\n{\n\tSCL = 0;//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;//拉高,读SDA\n\tdelay5us();\n\tif(SDA)//NOACK\n\t{\n\t\tSCL = 0;\n\t\treturn(1);//返回1\n\t}\n\telse//ACK\n\t{\n\t\tSCL = 0;\n\t\treturn(0);//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i)\n{\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT)\n{\n\tuchar i; \n\tfor(i=0; i<8; i++) //分别写8次,每次写1位\n\t{\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:Pcf8591DA(uchar Ctrl, DAT)\n参数\t:Ctrl 8591控制字节,DAT 要写入的数据\n返回值\t:无\n描述\t:PCF8591数字量转模拟量输出\n====================================*/\n//void Pcf8591DA(uchar Ctrl, DAT)\n//{\n//\tI2cStart();//I2C起始信号\n//\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n//\tif(ReadACK()) //读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cSendByte(Ctrl);//发送控制字节\n//\tif(ReadACK())//读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cSendByte(DAT);//发送一字节数据\n//\tif(ReadACK())//读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cStop();\t//I2C停止信号\n//}\n\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte()\n{\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++)//分别读8次,每次读一位\n\t{\n\t\tDAT <<= 1; //数据左移1位,准备接收一位\n\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); //返回读出的数据\n}\n\n/*====================================\n函数\t:PCF8591Read(uchar Ctrl)\n参数\t:Ctrl 8591控制字节\n返回值\t:AD转出的数字量\n描述\t:读指定通道的输入的模拟量专为数字量\n====================================*/\nuchar PCF8591Read(uchar Ctrl)\n{\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环\n{\t\n\twhile(1)\n\t{\n\t\tnum = (PCF8591Read(CH2) * 19.53 + 0.5);//读AD通道2,电位器值\n\t\t// 5000mv/256约等于等于19.53 ,加0.5是小数四舍五入接近实际转换精度\n\t\tdisplay(num); //数码管显示函数\n\t\tdelay(5);\n\t}\n} \n\n```\n\n\n\n### (二)\n\n使用I2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10亮度变化\n\n```c\n/*********************************************************************************\n* 【作 者】:\t清翔电子:向量\n* 【版 本】:\tV1.0\n* 【网 站】:\thttp://www.qxmcu.com/ \n* 【淘宝店铺】:\thttp://qxmcu.taobao.com/\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10\t\t \t\t\t \t\t\t \n* 【使用说明】: \t随着环境光线的变强LED10亮度增加,环境光变弱LED10亮度变暗\n\t\t\t\t 需要用跳线帽把DEN和DOUT短接(默认已短接)\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n#define PCF8591ADDR 0X90 //PCF8591硬件地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n#define\tCH0 0\t\t\t //AD通道0\n#define\tCH1\t1\t\t\t //AD通道1\n#define\tCH2\t2\t\t\t //AD通道2\n#define\tCH3\t3\t\t\t //AD通道3\n#define\tDAout\t0x40\t //DA输出命令\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1; //I2C时钟总线\nsbit SDA = P2^0; //I2C数据总线\nuchar num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n/*====================================\n函数\t: delay(uint z)\n参数\t:z 延时毫秒设定,取值范围0-65535\n返回值\t:无\n描述\t:12T/Fosc11.0592M毫秒级延时\n====================================*/\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示数值,取值范围0-255\n返回值\t:无\n描述\t:三位共阴数码管动态显示\n====================================*/\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid timer0Init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:delay5us()\n参数\t:无\n返回值\t:无\n描述\t:5us延时函数\n====================================*/\nvoid delay5us()\n{\n\t_nop_();\n}\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart()\n{\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop()\n{\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK()\n{\n\tSCL = 0;//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;//拉高,读SDA\n\tdelay5us();\n\tif(SDA)//NOACK\n\t{\n\t\tSCL = 0;\n\t\treturn(1);//返回1\n\t}\n\telse//ACK\n\t{\n\t\tSCL = 0;\n\t\treturn(0);//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i)\n{\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT)\n{\n\tuchar i; \n\tfor(i=0; i<8; i++) //分别写8次,每次写1位\n\t{\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:Pcf8591DA(uchar Ctrl, DAT)\n参数\t:Ctrl 8591控制字节,DAT 要写入的数据\n返回值\t:无\n描述\t:PCF8591数字量转模拟量输出\n====================================*/\nvoid Pcf8591DA(uchar Ctrl, DAT)\n{\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK()) //读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(DAT);//发送一字节数据\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cStop();\t//I2C停止信号\n}\n\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte()\n{\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++)//分别读8次,每次读一位\n\t{\n\t\tDAT <<= 1; //数据左移1位,准备接收一位\n\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); //返回读出的数据\n}\n\n/*====================================\n函数\t:PCF8591Read(uchar Ctrl)\n参数\t:Ctrl 8591控制字节\n返回值\t:AD转出的数字量\n描述\t:读指定通道的输入的模拟量专为数字量\n====================================*/\nuchar PCF8591Read(uchar Ctrl)\n{\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环\n{\t\n\ttimer0Init();//定时器0初始化\n\twhile(1)\n\t{\n\t\tEA = 0;//屏蔽中断\n\t\tnum = PCF8591Read(CH0);//读AD通道0,光敏值\n\t\tPcf8591DA(DAout, ~num);//把光敏转出的数字量取反,输出模拟量控制LED10\n\t\tEA = 1;//开中断\n\t\tdelay(5);\n\t}\n} \n\n//定时器0中断函数\nvoid timer0() interrupt 1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(五).md","raw":"---\ntitle: 51单片机学习笔记(五)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 逐次逼近式AD转换器原理介绍,PCF8591模块介绍\nabbrlink: c4d68191\ndate: 2021-08-19 19:02:48\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n\n\n\n\n## 逐次逼近式AD转换器\n\n\n\n这是一个基本原理图\n\n首先,内部产生一个参考电压Vref,然后与要进行测量的电压Vin进行比较,如果Vref<=Vin,就产生一个“1”信号(否则产生“0”信号),并存储到N位寄存器的最高位,然重新生成参考电压信号Vref = Vref+0.5Vref,(如果前面产生的是“0”信号,则新的参考电压Vref = 0.5Vref),再与Vin比较,将比较结果(“0”或“1”存到寄存器的下一位),循环往复,直到N位寄存器全部存满数据,最后通过锁存器输出转化后的数字信号。\n\n![image-20210824143628336](https://gitee.com/ajream/images/raw/master/img/20210824143631_image-20210824143628336.png)\n\n比如:Vin = 3.75V, Vref = 2.5V\n\n1. Vref=2.5 < 3.75,因此寄存器最高位存储数据1,然后重新生成Vref = 2.5+0.5*2.5 = 3.75\n2. 此时Vref=3.75 == Vin, 返回比较结果1, 存储下来, 然后重新生成Vref = 3.75+3.75*0.5 = 5.6\n3. 此时Vref=5.6 > Vin,返回比较结果0,存储下来。。。。。\n4. 最后结果是:`1100 0000`\n\n\n\n\n\n## PCF8591模块\n\n\n\n### 简介\n\nPCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入、一个输出和一个串行 I2C 总线接口;\n\n3个地址引脚A0、A1 和 A2 用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件;\n\nPCF8591由于其使用的简单方便和集成度高,在单片机应用系统中得到了广泛的应用。\n\n特点:\n\n1. 单电源供电\n\n2. 工作电压:2.5 V ~ 6 V\n\n3. I2C总线串行输入/输出\n\n4. 通过3个硬件地址引脚编址\n\n5. 采样速率取决于 I2C 总线传输速率决定\n\n6. 4个模拟输入可编程为单端或差分输入\n\n7. 自动增量通道选择\n\n8. 8位逐次比较型 A/D 转换\n\n\n\n管脚定义:\n\n![image-20210824145451094](https://gitee.com/ajream/images/raw/master/img/20210824145452_image-20210824145451094.png)\n\n1. AIN0~AIN3:模拟量输入通道\n2. AOUT:模拟输出通道\n3. A0~A2:硬件设备地址\n4. VDD:电源正极\n5. VSS:电源负极\n6. VREF:参考电压输入。\n7. EXT:振荡器输入时,内部/外部的切换开关。\n8. OSC:振荡器输入/输出。\n9. SCL:I2C BUS时钟输入。\n10. SDA:I2C BUS 数据输入/输出。\n11. AGND:模拟地,模拟信号和基准电源的参考地\n\n\n\n\n\n开发板中的PCF8591接线:\n\n![image-20210824145815414](https://gitee.com/ajream/images/raw/master/img/20210824145816_image-20210824145815414.png)\n\n\n\n\n\n### PCF8591地址\n\nI2C 总线系统中的每一片PCF8591 通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分\n\n可编程部分必须根据地址引脚A0、A1 和 A2 来设置,因此I2C系统中最多可接8个PCF8591\n\n在I2C 总线协议中地址必须是起始条件后作为第一个字节发送。\n\n地址字节的最后一位是用于设置以后数据传输方向的读/写位(1为读操作,0为写操作)\n\n\n\n![image-20210824150249772](https://gitee.com/ajream/images/raw/master/img/20210824150251_image-20210824150249772.png)\n\n\n\n\n\n### PCF8591控制字节\n\n发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。\n\n控制寄存器的高半字节用于允许模拟输出,以及可以将模拟输入编程为单端或差分输入。\n\n低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置1,每次A/D 转换后通道号将自动增加。\n\n\n\n\n\n\n\n\n\n## 例子\n\n### (一)\n\n利用I2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,通过数码管显示\n\n```c\n/*********************************************************************************\n* 【作 者】:\t清翔电子:向量\n* 【版 本】:\tV1.0\n* 【网 站】:\thttp://www.qxmcu.com/ \n* 【淘宝店铺】:\thttp://qxmcu.taobao.com/\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,数码管\n\t\t\t\t\t显示。\t\t \t\t\t \t\t\t \n* 【使用说明】: \t用手转动AD旁边的电位器头,数码管数值会随之变化。\n**********************************************************************************/\n#include \n#include \n\n#define PCF8591ADDR 0X90 //PCF8591地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n#define\tCH0 0\t\t\t //AD通道0\n#define\tCH1\t1\t\t\t //AD通道1\n#define\tCH2\t2\t\t\t //AD通道2\n#define\tCH3\t3\t\t\t //AD通道3\n#define\tDAout\t0x40\t //DA输出命令\n\n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n使用typedef给已有数据类型取别名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\ntypedef unsigned char u8;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\ntypedef unsigned int u16;\n\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1; //I2C时钟总线\nsbit SDA = P2^0; //I2C数据总线\nuint num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\n//数码管位选码 //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位 \nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数名\t:void delay(INT16U ms)\n参数\t:ms,毫秒延时形参\n返回值\t:无\n描述\t:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid delay(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n/*====================================\n函数\t:void delay5us()\n参数\t:无\n返回值\t:无\n描述\t:12T 51单片机5微秒延时函数自适应时钟\n====================================*/\nvoid delay5us()\n{\n\t#if MAIN_Fosc == 11059200\n\t\t_nop_();\n\t#elif MAIN_Fosc == 12000000\n\t\t_nop_();\n\t#elif MAIN_Fosc == 22118400\n\t\t_nop_(); _nop_(); _nop_();\n\t#endif\n}\n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示变量取值0-65535 \n返回值\t:无\n描述\t:数码管动态显示函数第一位显示小数点\n====================================*/\n//void display(uint i)\n//{\n//\tstatic uchar wei;\n//\t \t\t\n//\tP0 = 0XFF;//清除断码\n//\tWE = 1;//打开位选锁存器\n//\tP0 = SMGwei[wei];\n//\tWE = 0;//锁存位选数据\n//\n//\tswitch(wei)\n//\t{\n//\t\tcase 0: DU = 1; P0 = SMGduan[i / 1000] | 0x80; \tDU = 0; break;//千位显示小数点\n//\t\tcase 1: DU = 1; P0 = SMGduan[i % 1000 / 100]; \tDU = 0; break;//显示百位\n//\t\tcase 2: DU = 1; P0 = SMGduan[i % 100 / 10]; \tDU = 0; break;//显示十位\t\n//\t\tcase 3: DU = 1; P0 = SMGduan[i % 10]; \t\t\tDU = 0; break;//显示个位\t\t\n//\t}\n//\twei++;\n//\tif(wei == 4)\n//\t\twei = 0;\n//}\n\n/*====================================\n函数:display(INT16U Value)\n参数:Value,显示值 取值0-65535\n描述:共阴极数码管显示函数可显示一个字节的数\n====================================*/\nvoid display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型\n{\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\tP0 = SMGduan[Value/1000]|0x80;\t//数码管显示千位\n\tDU = 1;\t\t\t\t\t\t\t//打开段选\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tP0 = SMGwei[0];\t\t\t\t //第一位数码管\n\tWE = 1;\t\t\t\t\t\t//打开位选\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%1000/100]; //显示百位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[1];\t\t\t //第二位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%100/10];\t\t//显示十位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[2];\t\t\t\t//第三位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%10];\t\t//显示个位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[3];\t\t\t\t//第四位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n}\n\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart()\n{\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop()\n{\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK()\n{\n\tSCL = 0;//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;//拉高,读SDA\n\tdelay5us();\n\tif(SDA)//NOACK\n\t{\n\t\tSCL = 0;\n\t\treturn(1);//返回1\n\t}\n\telse//ACK\n\t{\n\t\tSCL = 0;\n\t\treturn(0);//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i)\n{\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT)\n{\n\tuchar i; \n\tfor(i=0; i<8; i++) //分别写8次,每次写1位\n\t{\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:Pcf8591DA(uchar Ctrl, DAT)\n参数\t:Ctrl 8591控制字节,DAT 要写入的数据\n返回值\t:无\n描述\t:PCF8591数字量转模拟量输出\n====================================*/\n//void Pcf8591DA(uchar Ctrl, DAT)\n//{\n//\tI2cStart();//I2C起始信号\n//\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n//\tif(ReadACK()) //读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cSendByte(Ctrl);//发送控制字节\n//\tif(ReadACK())//读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cSendByte(DAT);//发送一字节数据\n//\tif(ReadACK())//读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cStop();\t//I2C停止信号\n//}\n\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte()\n{\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++)//分别读8次,每次读一位\n\t{\n\t\tDAT <<= 1; //数据左移1位,准备接收一位\n\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); //返回读出的数据\n}\n\n/*====================================\n函数\t:PCF8591Read(uchar Ctrl)\n参数\t:Ctrl 8591控制字节\n返回值\t:AD转出的数字量\n描述\t:读指定通道的输入的模拟量专为数字量\n====================================*/\nuchar PCF8591Read(uchar Ctrl)\n{\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环\n{\t\n\twhile(1)\n\t{\n\t\tnum = (PCF8591Read(CH2) * 19.53 + 0.5);//读AD通道2,电位器值\n\t\t// 5000mv/256约等于等于19.53 ,加0.5是小数四舍五入接近实际转换精度\n\t\tdisplay(num); //数码管显示函数\n\t\tdelay(5);\n\t}\n} \n\n```\n\n\n\n### (二)\n\n使用I2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10亮度变化\n\n```c\n/*********************************************************************************\n* 【作 者】:\t清翔电子:向量\n* 【版 本】:\tV1.0\n* 【网 站】:\thttp://www.qxmcu.com/ \n* 【淘宝店铺】:\thttp://qxmcu.taobao.com/\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10\t\t \t\t\t \t\t\t \n* 【使用说明】: \t随着环境光线的变强LED10亮度增加,环境光变弱LED10亮度变暗\n\t\t\t\t 需要用跳线帽把DEN和DOUT短接(默认已短接)\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n#define PCF8591ADDR 0X90 //PCF8591硬件地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n#define\tCH0 0\t\t\t //AD通道0\n#define\tCH1\t1\t\t\t //AD通道1\n#define\tCH2\t2\t\t\t //AD通道2\n#define\tCH3\t3\t\t\t //AD通道3\n#define\tDAout\t0x40\t //DA输出命令\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1; //I2C时钟总线\nsbit SDA = P2^0; //I2C数据总线\nuchar num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n/*====================================\n函数\t: delay(uint z)\n参数\t:z 延时毫秒设定,取值范围0-65535\n返回值\t:无\n描述\t:12T/Fosc11.0592M毫秒级延时\n====================================*/\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示数值,取值范围0-255\n返回值\t:无\n描述\t:三位共阴数码管动态显示\n====================================*/\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid timer0Init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:delay5us()\n参数\t:无\n返回值\t:无\n描述\t:5us延时函数\n====================================*/\nvoid delay5us()\n{\n\t_nop_();\n}\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart()\n{\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop()\n{\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK()\n{\n\tSCL = 0;//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;//拉高,读SDA\n\tdelay5us();\n\tif(SDA)//NOACK\n\t{\n\t\tSCL = 0;\n\t\treturn(1);//返回1\n\t}\n\telse//ACK\n\t{\n\t\tSCL = 0;\n\t\treturn(0);//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i)\n{\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT)\n{\n\tuchar i; \n\tfor(i=0; i<8; i++) //分别写8次,每次写1位\n\t{\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:Pcf8591DA(uchar Ctrl, DAT)\n参数\t:Ctrl 8591控制字节,DAT 要写入的数据\n返回值\t:无\n描述\t:PCF8591数字量转模拟量输出\n====================================*/\nvoid Pcf8591DA(uchar Ctrl, DAT)\n{\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK()) //读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(DAT);//发送一字节数据\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cStop();\t//I2C停止信号\n}\n\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte()\n{\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++)//分别读8次,每次读一位\n\t{\n\t\tDAT <<= 1; //数据左移1位,准备接收一位\n\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); //返回读出的数据\n}\n\n/*====================================\n函数\t:PCF8591Read(uchar Ctrl)\n参数\t:Ctrl 8591控制字节\n返回值\t:AD转出的数字量\n描述\t:读指定通道的输入的模拟量专为数字量\n====================================*/\nuchar PCF8591Read(uchar Ctrl)\n{\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环\n{\t\n\ttimer0Init();//定时器0初始化\n\twhile(1)\n\t{\n\t\tEA = 0;//屏蔽中断\n\t\tnum = PCF8591Read(CH0);//读AD通道0,光敏值\n\t\tPcf8591DA(DAout, ~num);//把光敏转出的数字量取反,输出模拟量控制LED10\n\t\tEA = 1;//开中断\n\t\tdelay(5);\n\t}\n} \n\n//定时器0中断函数\nvoid timer0() interrupt 1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(五)","published":1,"updated":"2021-09-10T12:45:42.618Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6tt00h0akvehg5w1fi3","content":"

逐次逼近式AD转换器

这是一个基本原理图

\n

首先,内部产生一个参考电压Vref,然后与要进行测量的电压Vin进行比较,如果Vref<=Vin,就产生一个“1”信号(否则产生“0”信号),并存储到N位寄存器的最高位,然重新生成参考电压信号Vref = Vref+0.5Vref,(如果前面产生的是“0”信号,则新的参考电压Vref = 0.5Vref),再与Vin比较,将比较结果(“0”或“1”存到寄存器的下一位),循环往复,直到N位寄存器全部存满数据,最后通过锁存器输出转化后的数字信号。

\n

\"image-20210824143628336\"

\n

比如:Vin = 3.75V, Vref = 2.5V

\n
    \n
  1. Vref=2.5 < 3.75,因此寄存器最高位存储数据1,然后重新生成Vref = 2.5+0.5*2.5 = 3.75
  2. \n
  3. 此时Vref=3.75 == Vin, 返回比较结果1, 存储下来, 然后重新生成Vref = 3.75+3.75*0.5 = 5.6
  4. \n
  5. 此时Vref=5.6 > Vin,返回比较结果0,存储下来。。。。。
  6. \n
  7. 最后结果是:1100 0000
  8. \n
\n

PCF8591模块

简介

PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入、一个输出和一个串行 I2C 总线接口;

\n

3个地址引脚A0、A1 和 A2 用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件;

\n

PCF8591由于其使用的简单方便和集成度高,在单片机应用系统中得到了广泛的应用。

\n

特点:

\n
    \n
  1. 单电源供电

    \n
  2. \n
  3. 工作电压:2.5 V ~ 6 V

    \n
  4. \n
  5. I2C总线串行输入/输出

    \n
  6. \n
  7. 通过3个硬件地址引脚编址

    \n
  8. \n
  9. 采样速率取决于 I2C 总线传输速率决定

    \n
  10. \n
  11. 4个模拟输入可编程为单端或差分输入

    \n
  12. \n
  13. 自动增量通道选择

    \n
  14. \n
  15. 8位逐次比较型 A/D 转换

    \n
  16. \n
\n

管脚定义:

\n

\"image-20210824145451094\"

\n
    \n
  1. AIN0~AIN3:模拟量输入通道
  2. \n
  3. AOUT:模拟输出通道
  4. \n
  5. A0~A2:硬件设备地址
  6. \n
  7. VDD:电源正极
  8. \n
  9. VSS:电源负极
  10. \n
  11. VREF:参考电压输入。
  12. \n
  13. EXT:振荡器输入时,内部/外部的切换开关。
  14. \n
  15. OSC:振荡器输入/输出。
  16. \n
  17. SCL:I2C BUS时钟输入。
  18. \n
  19. SDA:I2C BUS 数据输入/输出。
  20. \n
  21. AGND:模拟地,模拟信号和基准电源的参考地
  22. \n
\n

开发板中的PCF8591接线:

\n

\"image-20210824145815414\"

\n

PCF8591地址

I2C 总线系统中的每一片PCF8591 通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分

\n

可编程部分必须根据地址引脚A0、A1 和 A2 来设置,因此I2C系统中最多可接8个PCF8591

\n

在I2C 总线协议中地址必须是起始条件后作为第一个字节发送。

\n

地址字节的最后一位是用于设置以后数据传输方向的读/写位(1为读操作,0为写操作)

\n

\"image-20210824150249772\"

\n

PCF8591控制字节

发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。

\n

控制寄存器的高半字节用于允许模拟输出,以及可以将模拟输入编程为单端或差分输入。

\n

低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置1,每次A/D 转换后通道号将自动增加。

\n

例子

(一)

利用I2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,通过数码管显示

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*********************************************************************************
* 【作 者】:\t清翔电子:向量
* 【版 本】:\tV1.0
* 【网 站】:\thttp://www.qxmcu.com/
* 【淘宝店铺】:\thttp://qxmcu.taobao.com/
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,数码管
\t\t\t\t\t显示。\t\t \t\t\t \t\t\t
* 【使用说明】: \t用手转动AD旁边的电位器头,数码管数值会随之变化。
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define PCF8591ADDR 0X90 //PCF8591地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位
#define\tCH0 0\t\t\t //AD通道0
#define\tCH1\t1\t\t\t //AD通道1
#define\tCH2\t2\t\t\t //AD通道2
#define\tCH3\t3\t\t\t //AD通道3
#define\tDAout\t0x40\t //DA输出命令

#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
使用typedef给已有数据类型取别名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;
typedef unsigned char u8;

typedef unsigned int INT16U;
typedef unsigned int uint;
typedef unsigned int u16;


sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1; //I2C时钟总线
sbit SDA = P2^0; //I2C数据总线
uint num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

//数码管位选码 //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数名\t:void delay(INT16U ms)
参数\t:ms,毫秒延时形参
返回值\t:无
描述\t:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void delay(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}
/*====================================
函数\t:void delay5us()
参数\t:无
返回值\t:无
描述\t:12T 51单片机5微秒延时函数自适应时钟
====================================*/
void delay5us()
{
\t#if MAIN_Fosc == 11059200
\t\t_nop_();
\t#elif MAIN_Fosc == 12000000
\t\t_nop_();
\t#elif MAIN_Fosc == 22118400
\t\t_nop_(); _nop_(); _nop_();
\t#endif
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示变量取值0-65535
返回值\t:无
描述\t:数码管动态显示函数第一位显示小数点
====================================*/
//void display(uint i)
//{
//\tstatic uchar wei;
//\t \t\t
//\tP0 = 0XFF;//清除断码
//\tWE = 1;//打开位选锁存器
//\tP0 = SMGwei[wei];
//\tWE = 0;//锁存位选数据
//
//\tswitch(wei)
//\t{
//\t\tcase 0: DU = 1; P0 = SMGduan[i / 1000] | 0x80; \tDU = 0; break;//千位显示小数点
//\t\tcase 1: DU = 1; P0 = SMGduan[i % 1000 / 100]; \tDU = 0; break;//显示百位
//\t\tcase 2: DU = 1; P0 = SMGduan[i % 100 / 10]; \tDU = 0; break;//显示十位\t
//\t\tcase 3: DU = 1; P0 = SMGduan[i % 10]; \t\t\tDU = 0; break;//显示个位\t\t
//\t}
//\twei++;
//\tif(wei == 4)
//\t\twei = 0;
//}

/*====================================
函数:display(INT16U Value)
参数:Value,显示值 取值0-65535
描述:共阴极数码管显示函数可显示一个字节的数
====================================*/
void display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型
{\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t//关闭段选
\tP0 = SMGduan[Value/1000]|0x80;\t//数码管显示千位
\tDU = 1;\t\t\t\t\t\t\t//打开段选
\tDU = 0;\t\t\t\t\t\t\t//关闭段选

\tWE = 0;\t\t\t\t\t\t//关闭位选
\tP0 = SMGwei[0];\t\t\t\t //第一位数码管
\tWE = 1;\t\t\t\t\t\t//打开位选
\tWE = 0;\t\t\t\t\t\t//关闭位选
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%1000/100]; //显示百位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[1];\t\t\t //第二位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%100/10];\t\t//显示十位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[2];\t\t\t\t//第三位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%10];\t\t//显示个位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[3];\t\t\t\t//第四位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
}

/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart()
{
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop()
{
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK()
{
\tSCL = 0;//拉低时钟总线,允许从机控制SDA
\tSCL = 1;//拉高,读SDA
\tdelay5us();
\tif(SDA)//NOACK
\t{
\t\tSCL = 0;
\t\treturn(1);//返回1
\t}
\telse//ACK
\t{
\t\tSCL = 0;
\t\treturn(0);//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i)
{
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT)
{
\tuchar i;
\tfor(i=0; i<8; i++) //分别写8次,每次写1位
\t{
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:Pcf8591DA(uchar Ctrl, DAT)
参数\t:Ctrl 8591控制字节,DAT 要写入的数据
返回值\t:无
描述\t:PCF8591数字量转模拟量输出
====================================*/
//void Pcf8591DA(uchar Ctrl, DAT)
//{
//\tI2cStart();//I2C起始信号
//\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
//\tif(ReadACK()) //读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cSendByte(Ctrl);//发送控制字节
//\tif(ReadACK())//读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cSendByte(DAT);//发送一字节数据
//\tif(ReadACK())//读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cStop();\t//I2C停止信号
//}


/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte()
{
\tuchar i, DAT;
\tfor(i=0; i<8; i++)//分别读8次,每次读一位
\t{
\t\tDAT <<= 1; //数据左移1位,准备接收一位
\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); //返回读出的数据
}

/*====================================
函数\t:PCF8591Read(uchar Ctrl)
参数\t:Ctrl 8591控制字节
返回值\t:AD转出的数字量
描述\t:读指定通道的输入的模拟量专为数字量
====================================*/
uchar PCF8591Read(uchar Ctrl)
{
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环
{\t
\twhile(1)
\t{
\t\tnum = (PCF8591Read(CH2) * 19.53 + 0.5);//读AD通道2,电位器值
\t\t// 5000mv/256约等于等于19.53 ,加0.5是小数四舍五入接近实际转换精度
\t\tdisplay(num); //数码管显示函数
\t\tdelay(5);
\t}
}

\n

(二)

使用I2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10亮度变化

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/*********************************************************************************
* 【作 者】:\t清翔电子:向量
* 【版 本】:\tV1.0
* 【网 站】:\thttp://www.qxmcu.com/
* 【淘宝店铺】:\thttp://qxmcu.taobao.com/
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10\t\t \t\t\t \t\t\t
* 【使用说明】: \t随着环境光线的变强LED10亮度增加,环境光变弱LED10亮度变暗
\t\t\t\t 需要用跳线帽把DEN和DOUT短接(默认已短接)
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char
#define PCF8591ADDR 0X90 //PCF8591硬件地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位
#define\tCH0 0\t\t\t //AD通道0
#define\tCH1\t1\t\t\t //AD通道1
#define\tCH2\t2\t\t\t //AD通道2
#define\tCH3\t3\t\t\t //AD通道3
#define\tDAout\t0x40\t //DA输出命令

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1; //I2C时钟总线
sbit SDA = P2^0; //I2C数据总线
uchar num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

/*====================================
函数\t: delay(uint z)
参数\t:z 延时毫秒设定,取值范围0-65535
返回值\t:无
描述\t:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示数值,取值范围0-255
返回值\t:无
描述\t:三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void timer0Init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}
/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:delay5us()
参数\t:无
返回值\t:无
描述\t:5us延时函数
====================================*/
void delay5us()
{
\t_nop_();
}

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart()
{
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop()
{
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK()
{
\tSCL = 0;//拉低时钟总线,允许从机控制SDA
\tSCL = 1;//拉高,读SDA
\tdelay5us();
\tif(SDA)//NOACK
\t{
\t\tSCL = 0;
\t\treturn(1);//返回1
\t}
\telse//ACK
\t{
\t\tSCL = 0;
\t\treturn(0);//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i)
{
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT)
{
\tuchar i;
\tfor(i=0; i<8; i++) //分别写8次,每次写1位
\t{
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:Pcf8591DA(uchar Ctrl, DAT)
参数\t:Ctrl 8591控制字节,DAT 要写入的数据
返回值\t:无
描述\t:PCF8591数字量转模拟量输出
====================================*/
void Pcf8591DA(uchar Ctrl, DAT)
{
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK()) //读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(DAT);//发送一字节数据
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cStop();\t//I2C停止信号
}


/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte()
{
\tuchar i, DAT;
\tfor(i=0; i<8; i++)//分别读8次,每次读一位
\t{
\t\tDAT <<= 1; //数据左移1位,准备接收一位
\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); //返回读出的数据
}

/*====================================
函数\t:PCF8591Read(uchar Ctrl)
参数\t:Ctrl 8591控制字节
返回值\t:AD转出的数字量
描述\t:读指定通道的输入的模拟量专为数字量
====================================*/
uchar PCF8591Read(uchar Ctrl)
{
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环
{\t
\ttimer0Init();//定时器0初始化
\twhile(1)
\t{
\t\tEA = 0;//屏蔽中断
\t\tnum = PCF8591Read(CH0);//读AD通道0,光敏值
\t\tPcf8591DA(DAout, ~num);//把光敏转出的数字量取反,输出模拟量控制LED10
\t\tEA = 1;//开中断
\t\tdelay(5);
\t}
}

//定时器0中断函数
void timer0() interrupt 1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

逐次逼近式AD转换器

这是一个基本原理图

\n

首先,内部产生一个参考电压Vref,然后与要进行测量的电压Vin进行比较,如果Vref<=Vin,就产生一个“1”信号(否则产生“0”信号),并存储到N位寄存器的最高位,然重新生成参考电压信号Vref = Vref+0.5Vref,(如果前面产生的是“0”信号,则新的参考电压Vref = 0.5Vref),再与Vin比较,将比较结果(“0”或“1”存到寄存器的下一位),循环往复,直到N位寄存器全部存满数据,最后通过锁存器输出转化后的数字信号。

\n

\"image-20210824143628336\"

\n

比如:Vin = 3.75V, Vref = 2.5V

\n
    \n
  1. Vref=2.5 < 3.75,因此寄存器最高位存储数据1,然后重新生成Vref = 2.5+0.5*2.5 = 3.75
  2. \n
  3. 此时Vref=3.75 == Vin, 返回比较结果1, 存储下来, 然后重新生成Vref = 3.75+3.75*0.5 = 5.6
  4. \n
  5. 此时Vref=5.6 > Vin,返回比较结果0,存储下来。。。。。
  6. \n
  7. 最后结果是:1100 0000
  8. \n
\n

PCF8591模块

简介

PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入、一个输出和一个串行 I2C 总线接口;

\n

3个地址引脚A0、A1 和 A2 用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件;

\n

PCF8591由于其使用的简单方便和集成度高,在单片机应用系统中得到了广泛的应用。

\n

特点:

\n
    \n
  1. 单电源供电

    \n
  2. \n
  3. 工作电压:2.5 V ~ 6 V

    \n
  4. \n
  5. I2C总线串行输入/输出

    \n
  6. \n
  7. 通过3个硬件地址引脚编址

    \n
  8. \n
  9. 采样速率取决于 I2C 总线传输速率决定

    \n
  10. \n
  11. 4个模拟输入可编程为单端或差分输入

    \n
  12. \n
  13. 自动增量通道选择

    \n
  14. \n
  15. 8位逐次比较型 A/D 转换

    \n
  16. \n
\n

管脚定义:

\n

\"image-20210824145451094\"

\n
    \n
  1. AIN0~AIN3:模拟量输入通道
  2. \n
  3. AOUT:模拟输出通道
  4. \n
  5. A0~A2:硬件设备地址
  6. \n
  7. VDD:电源正极
  8. \n
  9. VSS:电源负极
  10. \n
  11. VREF:参考电压输入。
  12. \n
  13. EXT:振荡器输入时,内部/外部的切换开关。
  14. \n
  15. OSC:振荡器输入/输出。
  16. \n
  17. SCL:I2C BUS时钟输入。
  18. \n
  19. SDA:I2C BUS 数据输入/输出。
  20. \n
  21. AGND:模拟地,模拟信号和基准电源的参考地
  22. \n
\n

开发板中的PCF8591接线:

\n

\"image-20210824145815414\"

\n

PCF8591地址

I2C 总线系统中的每一片PCF8591 通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分

\n

可编程部分必须根据地址引脚A0、A1 和 A2 来设置,因此I2C系统中最多可接8个PCF8591

\n

在I2C 总线协议中地址必须是起始条件后作为第一个字节发送。

\n

地址字节的最后一位是用于设置以后数据传输方向的读/写位(1为读操作,0为写操作)

\n

\"image-20210824150249772\"

\n

PCF8591控制字节

发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。

\n

控制寄存器的高半字节用于允许模拟输出,以及可以将模拟输入编程为单端或差分输入。

\n

低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置1,每次A/D 转换后通道号将自动增加。

\n

例子

(一)

利用I2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,通过数码管显示

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*********************************************************************************
* 【作 者】:\t清翔电子:向量
* 【版 本】:\tV1.0
* 【网 站】:\thttp://www.qxmcu.com/
* 【淘宝店铺】:\thttp://qxmcu.taobao.com/
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,数码管
\t\t\t\t\t显示。\t\t \t\t\t \t\t\t
* 【使用说明】: \t用手转动AD旁边的电位器头,数码管数值会随之变化。
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define PCF8591ADDR 0X90 //PCF8591地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位
#define\tCH0 0\t\t\t //AD通道0
#define\tCH1\t1\t\t\t //AD通道1
#define\tCH2\t2\t\t\t //AD通道2
#define\tCH3\t3\t\t\t //AD通道3
#define\tDAout\t0x40\t //DA输出命令

#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
使用typedef给已有数据类型取别名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;
typedef unsigned char u8;

typedef unsigned int INT16U;
typedef unsigned int uint;
typedef unsigned int u16;


sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1; //I2C时钟总线
sbit SDA = P2^0; //I2C数据总线
uint num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

//数码管位选码 //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数名\t:void delay(INT16U ms)
参数\t:ms,毫秒延时形参
返回值\t:无
描述\t:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void delay(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}
/*====================================
函数\t:void delay5us()
参数\t:无
返回值\t:无
描述\t:12T 51单片机5微秒延时函数自适应时钟
====================================*/
void delay5us()
{
\t#if MAIN_Fosc == 11059200
\t\t_nop_();
\t#elif MAIN_Fosc == 12000000
\t\t_nop_();
\t#elif MAIN_Fosc == 22118400
\t\t_nop_(); _nop_(); _nop_();
\t#endif
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示变量取值0-65535
返回值\t:无
描述\t:数码管动态显示函数第一位显示小数点
====================================*/
//void display(uint i)
//{
//\tstatic uchar wei;
//\t \t\t
//\tP0 = 0XFF;//清除断码
//\tWE = 1;//打开位选锁存器
//\tP0 = SMGwei[wei];
//\tWE = 0;//锁存位选数据
//
//\tswitch(wei)
//\t{
//\t\tcase 0: DU = 1; P0 = SMGduan[i / 1000] | 0x80; \tDU = 0; break;//千位显示小数点
//\t\tcase 1: DU = 1; P0 = SMGduan[i % 1000 / 100]; \tDU = 0; break;//显示百位
//\t\tcase 2: DU = 1; P0 = SMGduan[i % 100 / 10]; \tDU = 0; break;//显示十位\t
//\t\tcase 3: DU = 1; P0 = SMGduan[i % 10]; \t\t\tDU = 0; break;//显示个位\t\t
//\t}
//\twei++;
//\tif(wei == 4)
//\t\twei = 0;
//}

/*====================================
函数:display(INT16U Value)
参数:Value,显示值 取值0-65535
描述:共阴极数码管显示函数可显示一个字节的数
====================================*/
void display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型
{\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t//关闭段选
\tP0 = SMGduan[Value/1000]|0x80;\t//数码管显示千位
\tDU = 1;\t\t\t\t\t\t\t//打开段选
\tDU = 0;\t\t\t\t\t\t\t//关闭段选

\tWE = 0;\t\t\t\t\t\t//关闭位选
\tP0 = SMGwei[0];\t\t\t\t //第一位数码管
\tWE = 1;\t\t\t\t\t\t//打开位选
\tWE = 0;\t\t\t\t\t\t//关闭位选
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%1000/100]; //显示百位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[1];\t\t\t //第二位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%100/10];\t\t//显示十位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[2];\t\t\t\t//第三位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%10];\t\t//显示个位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[3];\t\t\t\t//第四位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
}

/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart()
{
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop()
{
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK()
{
\tSCL = 0;//拉低时钟总线,允许从机控制SDA
\tSCL = 1;//拉高,读SDA
\tdelay5us();
\tif(SDA)//NOACK
\t{
\t\tSCL = 0;
\t\treturn(1);//返回1
\t}
\telse//ACK
\t{
\t\tSCL = 0;
\t\treturn(0);//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i)
{
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT)
{
\tuchar i;
\tfor(i=0; i<8; i++) //分别写8次,每次写1位
\t{
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:Pcf8591DA(uchar Ctrl, DAT)
参数\t:Ctrl 8591控制字节,DAT 要写入的数据
返回值\t:无
描述\t:PCF8591数字量转模拟量输出
====================================*/
//void Pcf8591DA(uchar Ctrl, DAT)
//{
//\tI2cStart();//I2C起始信号
//\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
//\tif(ReadACK()) //读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cSendByte(Ctrl);//发送控制字节
//\tif(ReadACK())//读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cSendByte(DAT);//发送一字节数据
//\tif(ReadACK())//读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cStop();\t//I2C停止信号
//}


/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte()
{
\tuchar i, DAT;
\tfor(i=0; i<8; i++)//分别读8次,每次读一位
\t{
\t\tDAT <<= 1; //数据左移1位,准备接收一位
\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); //返回读出的数据
}

/*====================================
函数\t:PCF8591Read(uchar Ctrl)
参数\t:Ctrl 8591控制字节
返回值\t:AD转出的数字量
描述\t:读指定通道的输入的模拟量专为数字量
====================================*/
uchar PCF8591Read(uchar Ctrl)
{
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环
{\t
\twhile(1)
\t{
\t\tnum = (PCF8591Read(CH2) * 19.53 + 0.5);//读AD通道2,电位器值
\t\t// 5000mv/256约等于等于19.53 ,加0.5是小数四舍五入接近实际转换精度
\t\tdisplay(num); //数码管显示函数
\t\tdelay(5);
\t}
}

\n

(二)

使用I2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10亮度变化

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/*********************************************************************************
* 【作 者】:\t清翔电子:向量
* 【版 本】:\tV1.0
* 【网 站】:\thttp://www.qxmcu.com/
* 【淘宝店铺】:\thttp://qxmcu.taobao.com/
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10\t\t \t\t\t \t\t\t
* 【使用说明】: \t随着环境光线的变强LED10亮度增加,环境光变弱LED10亮度变暗
\t\t\t\t 需要用跳线帽把DEN和DOUT短接(默认已短接)
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char
#define PCF8591ADDR 0X90 //PCF8591硬件地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位
#define\tCH0 0\t\t\t //AD通道0
#define\tCH1\t1\t\t\t //AD通道1
#define\tCH2\t2\t\t\t //AD通道2
#define\tCH3\t3\t\t\t //AD通道3
#define\tDAout\t0x40\t //DA输出命令

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1; //I2C时钟总线
sbit SDA = P2^0; //I2C数据总线
uchar num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

/*====================================
函数\t: delay(uint z)
参数\t:z 延时毫秒设定,取值范围0-65535
返回值\t:无
描述\t:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示数值,取值范围0-255
返回值\t:无
描述\t:三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void timer0Init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}
/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:delay5us()
参数\t:无
返回值\t:无
描述\t:5us延时函数
====================================*/
void delay5us()
{
\t_nop_();
}

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart()
{
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop()
{
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK()
{
\tSCL = 0;//拉低时钟总线,允许从机控制SDA
\tSCL = 1;//拉高,读SDA
\tdelay5us();
\tif(SDA)//NOACK
\t{
\t\tSCL = 0;
\t\treturn(1);//返回1
\t}
\telse//ACK
\t{
\t\tSCL = 0;
\t\treturn(0);//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i)
{
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT)
{
\tuchar i;
\tfor(i=0; i<8; i++) //分别写8次,每次写1位
\t{
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:Pcf8591DA(uchar Ctrl, DAT)
参数\t:Ctrl 8591控制字节,DAT 要写入的数据
返回值\t:无
描述\t:PCF8591数字量转模拟量输出
====================================*/
void Pcf8591DA(uchar Ctrl, DAT)
{
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK()) //读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(DAT);//发送一字节数据
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cStop();\t//I2C停止信号
}


/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte()
{
\tuchar i, DAT;
\tfor(i=0; i<8; i++)//分别读8次,每次读一位
\t{
\t\tDAT <<= 1; //数据左移1位,准备接收一位
\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); //返回读出的数据
}

/*====================================
函数\t:PCF8591Read(uchar Ctrl)
参数\t:Ctrl 8591控制字节
返回值\t:AD转出的数字量
描述\t:读指定通道的输入的模拟量专为数字量
====================================*/
uchar PCF8591Read(uchar Ctrl)
{
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环
{\t
\ttimer0Init();//定时器0初始化
\twhile(1)
\t{
\t\tEA = 0;//屏蔽中断
\t\tnum = PCF8591Read(CH0);//读AD通道0,光敏值
\t\tPcf8591DA(DAout, ~num);//把光敏转出的数字量取反,输出模拟量控制LED10
\t\tEA = 1;//开中断
\t\tdelay(5);
\t}
}

//定时器0中断函数
void timer0() interrupt 1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}
\n"},{"title":"butterfly主题配置记录","description":"用于记录搭建Hexo博客的过程即butterfly主题的配置","abbrlink":"dc7f8ae1","date":"2021-08-29T02:20:29.000Z","swiper_index":10,"cover":"https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png","_content":"\n\n\n## 下载安装\n\n{% folding 参考店长文章 %}\n\n\n1. [快速开始](https://butterfly.js.org/posts/21cfbf15/)\n2. [主题页面](https://butterfly.js.org/posts/dc584b87/)\n3. [主题配置1](https://butterfly.js.org/posts/4aa8abbe/)\n4. [主题配置2](https://butterfly.js.org/posts/ceeb73f/)\n5. [进阶教程](https://butterfly.js.org/posts/4073eda/)\n\n{% endfolding %}\n\n\n\n## 页面主题颜色配置\n\n{% folding 配置 %}\n\n\n在主题配置文件`_config.butterfly.yml`修改即可\n\n```yaml\ntheme_color:\n enable: true\n main: \"#49B1F5\"\n paginator: \"#00c4b6\"\n button_hover: \"#FF7242\"\n text_selection: \"#00c4b6\"\n link_color: \"#99a9bf\"\n meta_color: \"#858585\"\n hr_color: \"#A4D8FA\"\n code_foreground: \"#F47466\"\n code_background: \"rgba(27, 31, 35, .05)\"\n toc_color: \"#00c4b6\"\n blockquote_padding_color: \"#49b1f5\"\n blockquote_background_color: \"#49b1f5\"\n\n```\n\n{% endfolding %}\n\n## 外挂标签\n\n参考小康博客\n{% folding 查看%}\n[小康博客butterfly主题使用文档](https://www.antmoe.com/posts/3b43914f/)\n{% endfolding %}\n\n\n## 全局背景透明渐变\n[小康博客](https://www.antmoe.com/posts/7198453/)\n\n\n\n\n\n\n\n\n## 首页置顶轮播图\n\n[糖果屋教程](https://www.akilar.top/posts/8e1264d1/)\n\n{% folding cyan , 步骤 %}\n\n1. 安装插件\n ```\n npm install hexo-butterfly-swiper --save\n ```\n\n2. 在站点文件 `_config.yml`添加如下内容:\n ```yml\n # hexo-butterfly-swiper\n # see https://akilar.top/posts/8e1264d1/\n swiper:\n enable: true # 开关\n priority: 5 #过滤器优先权\n enable_page: all # 应用页面\n timemode: date #date/updated\n layout: # 挂载容器类型\n type: id\n name: recent-posts\n index: 0\n default_descr: 再怎么看我也不知道怎么描述它的啦!\n custom_css: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/css/swiper.min.css #自定义swiper css依赖\n custom_js: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/js/swiper.min.js #自定义swiper js依赖\n ```\n\n| 参数 | 备选值/类型 | 释义 |\n| :------------ | :----------- | :----------------------------------------------------------- |\n| priority | number | 【可选】过滤器优先级,数值越小,执行越早,默认为10,选填 |\n| enable | true/false | 【必选】控制开关 |\n| enable_page | path/all | 【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为all |\n| timemode | date/updated | 【可选】时间显示,date为显示创建日期,updated为显示更新日期,默认为date |\n| layout.type | id/class | 【可选】挂载容器类型,填写id或class,不填则默认为id |\n| layout.name | text | 【必选】挂载容器名称 |\n| layout.index | 0和正整数 | 【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位 |\n| default_descr | text | 默认文章描述 |\n| custom_css | url | 【可选】自定义的swiper依赖项css链接 |\n| custom_js | url | 【可选】自定义的swiper依赖项加js链接 |\n\n3. 在文章的 `front_matter` 添加 `swiper_index` 配置项即可\n\n ```yml\n ---\n title: 文章标题\n date: 创建日期\n updated: 更新日期\n cover: 文章封面\n description: 文章描述\n swiper_index: 1 #置顶轮播图顺序,非负整数,数字越大越靠前\n ---\n ```\n\n{% endfolding %}\n\n\n\n\n\n## 首页卡片浮动显示wowjs\n\n{% folding blue, 插件版本%}\n\n[糖果屋教程](https://akilar.top/posts/abab51cf/)\n\n1. 安装插件\n\n ```\n npm install hexo-butterfly-wowjs --save\n ```\n\n2. 添加配置信息,以下为写法示例\n 在站点配置文件`_config.yml` 或者主题配置文件`_config.butterfly.yml` 中添加\n\n ```yml\n wowjs:\n enable: true #控制动画开关。true是打开,false是关闭\n priority: 10 #过滤器优先级\n mobile: false #移动端是否启用,默认移动端禁用\n animateitem:\n - class: recent-post-item #必填项,需要添加动画的元素的class\n style: animate__zoomIn #必填项,需要添加的动画\n duration: 2s #选填项,动画持续时间,单位可以是ms也可以是s。例如3s,700ms。\n delay: 1s #选填项,动画开始的延迟时间,单位可以是ms也可以是s。例如3s,700ms。\n offset: 100 #选填项,开始动画的距离(相对浏览器底部)\n iteration: 2 #选填项,动画重复的次数\n - class: card-widget\n style: animate__zoomIn\n ```\n\n\n\n| 参数 | 备选值 / 类型 | 释义 |\n| :-------------------- | :--------------------- | :----------------------------------------------------------- |\n| enable | true/false | 【必选】控制开关 |\n| priority | number | 【可选】过滤器优先级,数值越小,执行越早,默认为 10,选填 |\n| mobile | true/false | 控制移动端是否启用,默认移动端禁用 |\n| animateitem.class | class | 【可选】添加动画类名,只支持给 class 添加 |\n| animateitem.style | text | 【可选】动画样式,具体类型参考 [animate.css](https://animate.style/) |\n| animateitem.duration | time, 单位为 s 或 ms | 【可选】动画持续时间,单位可以是 ms 也可以是 s。例如 3s,700ms。 |\n| animateitem.delay | time, 单位为 s 或 ms | 【可选】动画开始的延迟时间,单位可以是 ms 也可以是 s。例如 3s,700ms。 |\n| animateitem.offset | number, 单位为 px | 【可选】开始动画的距离(相对浏览器底部)。 |\n| animateitem.iteration | number, 单位为 s 或 ms | 【可选】动画重复的次数 |\n\n\n\n{%endfolding%}\n\n\n## 看板娘配置\n\n[糖果屋教程](https://akilar.top/posts/5b8f515f/)\n\n\n看板娘不能换装,参考糖果屋的解决方法:\n\n{%folding blue, 点击展开%}\n使用糖果屋大佬配置好的本地化项目的路径:\n修改项目内的 `~\\live2d-widget\\autoload.js`, 修改 `cdnPath`\n\n```diff\n // 加载 waifu.css live2d.min.js waifu-tips.js\n if (screen.width >= 768) {\n \tPromise.all([\n \t\tloadExternalResource(live2d_path + \"waifu.css\", \"css\"),\n \t\tloadExternalResource(live2d_path + \"live2d.min.js\", \"js\"),\n \t\tloadExternalResource(live2d_path + \"waifu-tips.js\", \"js\")\n \t]).then(() => {\n \t\tinitWidget({\n \t\t\twaifuPath: live2d_path + \"waifu-tips.json\",\n \t\t\t//apiPath: \"https://live2d.fghrsh.net/api/\",\n- \t\t\tcdnPath: \"https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/\"\n+ \t\t\tcdnPath: \"https://live2d-api.vercel.app/\"\n//因为jsdelivr不支持50MB以上的包的加速,可能报403错误,所以用的vercel的CDN服务。\n//可以考虑clone大佬配置好的live2d_api仓库自己部署到其他更快的cdn服务上。\n \t\t});\n \t});\n }\n\n```\n\n{%endfolding%}\n\n\n## 主页分类磁铁配置\n\n[糖果屋教程](https://akilar.top/posts/a9131002/)\n\n{%folding blue, 点击展开%}\n\n(1)修改`[Blogroot]\\themes\\butterfly\\layout\\index.pug`, 添加6~8行代码,以下是添加后的结果(注意代码格式比较严格,可直接复制)\n\n```\nextends includes/layout.pug\n\nblock content\n include ./includes/mixins/post-ui.pug\n #recent-posts.recent-posts\n if theme.categoryBar.enable\n .recent-post-item(style='height:auto;width:100%;padding:0px;')\n #categoryBar!= list_categories(site.categories,{class: 'categoryBar',depth: 1})\n +postUI\n include includes/pagination.pug\n\n```\n\n(2)新建`[Blogroot]\\themes\\butterfly\\source\\css\\_layout\\categoryBar.styl`\n\n```\nif hexo-config('categoryBar.enable')\n #categoryBar\n width 100%!important\n ul\n &.categoryBar-list\n margin 5px 5px 0 5px!important\n padding 0!important\n\n li\n &.categoryBar-list-item\n font-weight bold\n display inline-block\n height 180px!important\n margin 5px .5% 0 .5%!important\n background-image linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(16, 16, 16, 0) 100%)\n border-radius 10px\n padding 25px 0 25px 25px!important\n box-shadow rgba(50, 50, 50, 0.3) 50px 50px 50px 50px inset\n overflow hidden\n background-size 100%!important\n background-position center!important\n &:hover\n background-size 110%!important\n box-shadow inset 500px 50px 50px 50px rgba(50,50,50, 0.6)\n span\n &.categoryBar-list-count\n &::after\n transition all .5s\n transform translate(-100%, 0)\n a\n &.categoryBar-list-link\n color white!important\n font-size 20px!important\n &::before\n content '|'!important\n color white!important\n font-size 20px!important\n &:after\n content ''\n position relative\n width 0\n bottom 0\n display block\n height 3px\n border-radius 3px\n background-color white\n &:hover\n &:after\n width 90%\n left 1%\n transition all 0.5s\n\n span\n &.categoryBar-list-count\n display block!important\n color white!important\n font-size 20px!important\n &::before\n content '\\f02d'!important\n padding-right 15px!important\n @extend .fontawesomeIcon\n &::after\n padding 5px\n display block!important\n color white!important\n font-size 20px!important\n position relative\n right -100%\n covers = hexo-config('categoryBar.cover')\n for cover,i in covers\n li.categoryBar-list-item:nth-child({i+1})\n background unquote(cover)\n descrs = hexo-config('categoryBar.descr')\n for descr,i in descrs\n li.categoryBar-list-item:nth-child({i+1})>span::after\n content descr!important\n if hexo-config('categoryBar.column') == 'odd'\n li\n &.categoryBar-list-item\n width 32.3%!important\n else if hexo-config('categoryBar.column') == 'even'\n li\n &.categoryBar-list-item\n width 24%!important\n @media screen and (max-width: 650px)\n li\n &.categoryBar-list-item\n width 48%!important\n height 150px!important\n margin 5px 1% 0 1%!important\n\n $caterow = hexo-config('categoryBar.row')?hexo-config('categoryBar.row'):2\n .categoryBar-list\n max-height 190px * $caterow\n overflow auto\n &::-webkit-scrollbar\n width 0!important\n @media screen and (max-width: 650px)\n .categoryBar-list\n max-height 160px * $caterow\n```\n\n(3)在`[Blogroot]\\_config.butterfly.yml`添加配置项:\n\n```yml\ncategoryBar:\n enable: true\n column: odd # 显示列数,odd:3列 | even:4列\n row: 1 #显示行数,默认两行,超过行数切换为滚动显示\n descr:\n - Hexo博客搭建记录\n - Java学习笔记\n - 电磁场课程学习笔记\n - 硬件学习过程记录\n cover:\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover1.webp')\n - '#abcdef' # HEX格式色值需要用''包裹,不然会被识别成注释\n - rgba(45,67,89,0.7)\n - linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(200,16 , 16, 0) 100%)\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover5.webp')\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover6.webp')\n\n```\n\n{%endfolding%}\n\n\n\n\n## 主页添加文章分类配置(与上一个样式不同)\n\n{%folding blue, 展开%}\n\n[前往小冰博客](https://zfe.space/post/hexo-magnet.html)\n\n效果如下:\n![磁体2.gif](https://cdn.nlark.com/yuque/0/2021/gif/8391485/1615908841586-e6737009-b2dd-4004-ae02-4c6d15f33701.gif)\n这个插件主要实现了以下功能:\n1.自定义 tags 或 categories 的排列和展示 \n2.自定义 tags 或 categories 的展示图标,名称 \n3.自定义排列的行数,默认 2 行\n\n**NPM 插件安装的部署方法**\n\n```powershell\nnpm i hexo-magnet --save\n\n# 或者\n\ncnpm i hexo-magnet --save\n```\n\n注意,一定要加`--save`,不然本地预览的时候可能不会显示!!!\n\n**新增网站根目录\\_config 配置项(不是主题的)**\n\n```yaml\nmagnet:\n enable: true\n priority: 1\n enable_page: /\n type: categories\n devide: 2\n display:\n - name: java\n display_name: aJreamのJava全栈学习\n icon: 📚\n - name: Hexo\n display_name: aJreamのHexo博客搭建&主题配置\n icon: 🎮\n - name: win10\n display_name: aJreamのWin10主题配置\n icon: 💻\n - name: 硬件学习\n display_name: aJreamの硬件学习\n icon: 🔧\n color_setting:\n text_color: black\n text_hover_color: white\n background_color: \"#f2f2f2\"\n background_hover_color: \"#aebfe3\"\n layout:\n type: id\n name: recent-posts\n index: 0\n temple_html: '
${temple_html_item}
'\n plus_style: \n```\n\n接下来来简单说明一下配置项的含义:\n\n> **enable_page**\n\n参数:/\n含义:路由地址,如 / 代表主页。/me/代表自我介绍页等等\n\n> **priority**\n\n参数:1\n含义:插件的叠放顺序,数字越大,叠放约靠前。如果你安装了 hexo-githubcalendar,请将`hexo-githubcalendar`npm 插件更新至`@1.2.3`版本。然后给 hexo-githubcalendar 添加`priority`参数。\n\n```yaml\ngithubcalendar:\n enable: true\n priority: 3 # 这里加上参数\n```\n\n> **type**\n\n参数:categories、tags\n含义:选择筛选分类还是标签\n\n> **devide**\n\n参数:2\n含义:表示分隔的列数,2 表示分为两列展示\n\n> **display**\n\n参数:\n\n```yaml\n- name: 教程 # 这里是tags或者categories的名称\n display_name: 小冰の魔改教程 # 这里是替换的名称\n icon: 📚 # 这里是展示的图标\n```\n\n含义:配置项,可自行设置,按照设置的顺序展示\n\n> **color_setting**\n\n参数:\n\n```yaml\ntext_color: black # 文字默认颜色\ntext_hover_color: white # 文字鼠标悬浮颜色\nbackground_color: \"#f2f2f2\" # 文字背景默认颜色\nbackground_hover_color: \"#b30070\" # 文字背景悬浮颜色\n```\n\n含义:颜色配置项,可自行设置\n\n> **layout**\n\n参数:type; (class&id)\n参数:name;\n参数:index;(数字)\n含义:如果说 gihubcalendar 是一幅画,那么这个 layout 就是指定了哪面墙来挂画\n而在 HTML 的是世界里有两种墙分别 type 为 id 和 class。\n其中在定义 class 的时候会出现多个 class 的情况,这时就需要使用 index,确定是哪一个。\n最后墙的名字即是 name;\n\n```html\n
\n \n
\n
\n \n
\n
\n
\n```\n\n> **temple_html**\n\n参数:html 模板字段\n含义:包含挂载容器\n\n```xml\n
\n
\n ${temple_html_item}\n
\n
\n```\n\n> **plus_style**\n\n参数:\"\"\n含义:提供可自定义的 style,如加入黑夜模式,也可以通过CSS来添加。\n{%endfolding%}\n\n### 添加黑夜模式\n\n在 `custom.css` 文件中添加以下代码,使其在黑夜模式下生效\n```css\n[data-theme=\"dark\"] .magnet_link_context {\n background: #201e1e;\n border-radius: 6px;\n color: white;\n \n}\n\n[data-theme=\"dark\"] .magnet_link_context:hover {\n background: #3b4042;\n}\n```\n修改各类的跳转链接:\n原本点击各个分类后,默认跳转的链接是 `_config.yml` 文件中配置的 url值 + \"/categories\" + 【各个分类】\n\n但为了能使部署在不同服务器上能够跳转到自己的分类下,修改了插件`/node_modules/hexo-magnet/index.js`的代码:\n将第61行(或者在附近某行)的代码 `href=\"${hexo.config.url}/${item.path}\"` 的 `${hexo.config.url}`删掉 ,修改后如下:\n\n```js\ntemple_html_item += ``;\n```\n\n\n### hexo 三连\n\n执行 hexo 三连\n\n```powershell\nhexo clean && hexo g && hexo s\n```\n\n即可发现已经成功部署。\n\n\n\n\n## 标题h1~h6美化\n\n[参考大佬](https://guole.fun/posts/butterfly-custom/#%E6%96%87%E7%AB%A0%E9%A1%B5H1-H6%E6%A0%B7%E5%BC%8F%E4%BF%AE%E6%94%B9)\n\n{%folding blue, 展开%}\nButterfly 在 H1~H6 样式上使用了 [fontawesome.com](https://fontawesome.com/v5.15/icons?from=io)上的图标,引用的是 Unicode 形式。可自行查找合适的。\n![image-20210908221739125](https://gitee.com/ajream/images/raw/master/img/20210908221743_image-20210908221739125.png)\n\n{%tabs 小风车, -1%}\n\n本站小风车样式\n\n```yml\nbeautify:\n enable: true\n field: post # site/post\n # title-prefix-icon: '\\f0c1' 原内容\n title-prefix-icon: '\\f185'\n title-prefix-icon-color: '#F47466'\n```\n\n\n\n\n\n让小风车转起来\n\n在 `/css/custom.css` 文件中,加入以下代码即可。\n\n转速、转向修改看注释\n\n\n```css\n/* 文章页H1-H6图标样式效果 */\nh1::before,\nh2::before,\nh3::before,\nh4::before,\nh5::before,\nh6::before {\n -webkit-animation: ccc 1.6s linear infinite;\n animation: ccc 1.6s linear infinite;\n /* 转速:1.6,数字越小转速越快 */\n}\n\n@-webkit-keyframes ccc {\n \n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg)\n }\n/* -1turn 为逆时针转动,1turn 为顺时针转动,相同数字部分记得统一修改: */\n to {\n -webkit-transform: rotate(-1turn);\n transform: rotate(-1turn)\n }\n}\n\n@keyframes ccc {\n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg)\n }\n\n to {\n -webkit-transform: rotate(-1turn);\n transform: rotate(-1turn)\n }\n}\n\n```\n\n\n\n\n\n小风车颜色、大小修改\n\n直接上代码\n\n```css\n#content-inner.layout h1::before {\n color: #ef50a8 ;\n margin-left: -1.55rem;\n font-size: 1.3rem;\n margin-top: -0.23rem;\n}\n#content-inner.layout h2::before {\n color: #fb7061 ;\n margin-left: -1.35rem;\n font-size: 1.1rem;\n margin-top: -0.12rem;\n}\n#content-inner.layout h3::before {\n color: #ffbf00 ;\n margin-left: -1.22rem;\n font-size: 0.95rem;\n margin-top: -0.09rem;\n}\n#content-inner.layout h4::before {\n color: #a9e000 ;\n margin-left: -1.05rem;\n font-size: 0.8rem;\n margin-top: -0.09rem;\n}\n#content-inner.layout h5::before {\n color: #57c850 ;\n margin-left: -0.9rem;\n font-size: 0.7rem;\n margin-top: 0.0rem;\n}\n#content-inner.layout h6::before {\n color: #5ec1e0 ;\n margin-left: -0.9rem;\n font-size: 0.66rem;\n margin-top: 0.0rem;\n}\n\n```\n\n\n\n\n小风车hover效果\n\n鼠标碰到小风车转速变慢及变色\n\n设置鼠标碰到标题时,小风车跟随标题变色,且像是被光标阻碍了,转速变慢。鼠标离开恢复转速。也可以设置为 none 鼠标碰到停止转动。\n\n```css\n\n#content-inner.layout h1:hover, #content-inner.layout h2:hover, #content-inner.layout h3:hover, #content-inner.layout h4:hover, #content-inner.layout h5:hover, #content-inner.layout h6:hover {\n color: #49b1f5 ;\n}\n#content-inner.layout h1:hover::before, #content-inner.layout h2:hover::before, #content-inner.layout h3:hover::before, #content-inner.layout h4:hover::before, #content-inner.layout h5:hover::before, #content-inner.layout h6:hover::before {\n color: #49b1f5 ;\n -webkit-animation: ccc 3.2s linear infinite ;\n animation: ccc 3.2s linear infinite ;\n}\n\n```\n\n\n\n{%endtabs%}\n\n{%endfolding%}","source":"_posts/Hexo/butterfly主题配置.md","raw":"---\ntitle: butterfly主题配置记录\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: 用于记录搭建Hexo博客的过程即butterfly主题的配置\nabbrlink: dc7f8ae1\ndate: 2021-08-29 10:20:29\nswiper_index: 10\ncover: https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png\n---\n\n\n\n## 下载安装\n\n{% folding 参考店长文章 %}\n\n\n1. [快速开始](https://butterfly.js.org/posts/21cfbf15/)\n2. [主题页面](https://butterfly.js.org/posts/dc584b87/)\n3. [主题配置1](https://butterfly.js.org/posts/4aa8abbe/)\n4. [主题配置2](https://butterfly.js.org/posts/ceeb73f/)\n5. [进阶教程](https://butterfly.js.org/posts/4073eda/)\n\n{% endfolding %}\n\n\n\n## 页面主题颜色配置\n\n{% folding 配置 %}\n\n\n在主题配置文件`_config.butterfly.yml`修改即可\n\n```yaml\ntheme_color:\n enable: true\n main: \"#49B1F5\"\n paginator: \"#00c4b6\"\n button_hover: \"#FF7242\"\n text_selection: \"#00c4b6\"\n link_color: \"#99a9bf\"\n meta_color: \"#858585\"\n hr_color: \"#A4D8FA\"\n code_foreground: \"#F47466\"\n code_background: \"rgba(27, 31, 35, .05)\"\n toc_color: \"#00c4b6\"\n blockquote_padding_color: \"#49b1f5\"\n blockquote_background_color: \"#49b1f5\"\n\n```\n\n{% endfolding %}\n\n## 外挂标签\n\n参考小康博客\n{% folding 查看%}\n[小康博客butterfly主题使用文档](https://www.antmoe.com/posts/3b43914f/)\n{% endfolding %}\n\n\n## 全局背景透明渐变\n[小康博客](https://www.antmoe.com/posts/7198453/)\n\n\n\n\n\n\n\n\n## 首页置顶轮播图\n\n[糖果屋教程](https://www.akilar.top/posts/8e1264d1/)\n\n{% folding cyan , 步骤 %}\n\n1. 安装插件\n ```\n npm install hexo-butterfly-swiper --save\n ```\n\n2. 在站点文件 `_config.yml`添加如下内容:\n ```yml\n # hexo-butterfly-swiper\n # see https://akilar.top/posts/8e1264d1/\n swiper:\n enable: true # 开关\n priority: 5 #过滤器优先权\n enable_page: all # 应用页面\n timemode: date #date/updated\n layout: # 挂载容器类型\n type: id\n name: recent-posts\n index: 0\n default_descr: 再怎么看我也不知道怎么描述它的啦!\n custom_css: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/css/swiper.min.css #自定义swiper css依赖\n custom_js: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/js/swiper.min.js #自定义swiper js依赖\n ```\n\n| 参数 | 备选值/类型 | 释义 |\n| :------------ | :----------- | :----------------------------------------------------------- |\n| priority | number | 【可选】过滤器优先级,数值越小,执行越早,默认为10,选填 |\n| enable | true/false | 【必选】控制开关 |\n| enable_page | path/all | 【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为all |\n| timemode | date/updated | 【可选】时间显示,date为显示创建日期,updated为显示更新日期,默认为date |\n| layout.type | id/class | 【可选】挂载容器类型,填写id或class,不填则默认为id |\n| layout.name | text | 【必选】挂载容器名称 |\n| layout.index | 0和正整数 | 【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位 |\n| default_descr | text | 默认文章描述 |\n| custom_css | url | 【可选】自定义的swiper依赖项css链接 |\n| custom_js | url | 【可选】自定义的swiper依赖项加js链接 |\n\n3. 在文章的 `front_matter` 添加 `swiper_index` 配置项即可\n\n ```yml\n ---\n title: 文章标题\n date: 创建日期\n updated: 更新日期\n cover: 文章封面\n description: 文章描述\n swiper_index: 1 #置顶轮播图顺序,非负整数,数字越大越靠前\n ---\n ```\n\n{% endfolding %}\n\n\n\n\n\n## 首页卡片浮动显示wowjs\n\n{% folding blue, 插件版本%}\n\n[糖果屋教程](https://akilar.top/posts/abab51cf/)\n\n1. 安装插件\n\n ```\n npm install hexo-butterfly-wowjs --save\n ```\n\n2. 添加配置信息,以下为写法示例\n 在站点配置文件`_config.yml` 或者主题配置文件`_config.butterfly.yml` 中添加\n\n ```yml\n wowjs:\n enable: true #控制动画开关。true是打开,false是关闭\n priority: 10 #过滤器优先级\n mobile: false #移动端是否启用,默认移动端禁用\n animateitem:\n - class: recent-post-item #必填项,需要添加动画的元素的class\n style: animate__zoomIn #必填项,需要添加的动画\n duration: 2s #选填项,动画持续时间,单位可以是ms也可以是s。例如3s,700ms。\n delay: 1s #选填项,动画开始的延迟时间,单位可以是ms也可以是s。例如3s,700ms。\n offset: 100 #选填项,开始动画的距离(相对浏览器底部)\n iteration: 2 #选填项,动画重复的次数\n - class: card-widget\n style: animate__zoomIn\n ```\n\n\n\n| 参数 | 备选值 / 类型 | 释义 |\n| :-------------------- | :--------------------- | :----------------------------------------------------------- |\n| enable | true/false | 【必选】控制开关 |\n| priority | number | 【可选】过滤器优先级,数值越小,执行越早,默认为 10,选填 |\n| mobile | true/false | 控制移动端是否启用,默认移动端禁用 |\n| animateitem.class | class | 【可选】添加动画类名,只支持给 class 添加 |\n| animateitem.style | text | 【可选】动画样式,具体类型参考 [animate.css](https://animate.style/) |\n| animateitem.duration | time, 单位为 s 或 ms | 【可选】动画持续时间,单位可以是 ms 也可以是 s。例如 3s,700ms。 |\n| animateitem.delay | time, 单位为 s 或 ms | 【可选】动画开始的延迟时间,单位可以是 ms 也可以是 s。例如 3s,700ms。 |\n| animateitem.offset | number, 单位为 px | 【可选】开始动画的距离(相对浏览器底部)。 |\n| animateitem.iteration | number, 单位为 s 或 ms | 【可选】动画重复的次数 |\n\n\n\n{%endfolding%}\n\n\n## 看板娘配置\n\n[糖果屋教程](https://akilar.top/posts/5b8f515f/)\n\n\n看板娘不能换装,参考糖果屋的解决方法:\n\n{%folding blue, 点击展开%}\n使用糖果屋大佬配置好的本地化项目的路径:\n修改项目内的 `~\\live2d-widget\\autoload.js`, 修改 `cdnPath`\n\n```diff\n // 加载 waifu.css live2d.min.js waifu-tips.js\n if (screen.width >= 768) {\n \tPromise.all([\n \t\tloadExternalResource(live2d_path + \"waifu.css\", \"css\"),\n \t\tloadExternalResource(live2d_path + \"live2d.min.js\", \"js\"),\n \t\tloadExternalResource(live2d_path + \"waifu-tips.js\", \"js\")\n \t]).then(() => {\n \t\tinitWidget({\n \t\t\twaifuPath: live2d_path + \"waifu-tips.json\",\n \t\t\t//apiPath: \"https://live2d.fghrsh.net/api/\",\n- \t\t\tcdnPath: \"https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/\"\n+ \t\t\tcdnPath: \"https://live2d-api.vercel.app/\"\n//因为jsdelivr不支持50MB以上的包的加速,可能报403错误,所以用的vercel的CDN服务。\n//可以考虑clone大佬配置好的live2d_api仓库自己部署到其他更快的cdn服务上。\n \t\t});\n \t});\n }\n\n```\n\n{%endfolding%}\n\n\n## 主页分类磁铁配置\n\n[糖果屋教程](https://akilar.top/posts/a9131002/)\n\n{%folding blue, 点击展开%}\n\n(1)修改`[Blogroot]\\themes\\butterfly\\layout\\index.pug`, 添加6~8行代码,以下是添加后的结果(注意代码格式比较严格,可直接复制)\n\n```\nextends includes/layout.pug\n\nblock content\n include ./includes/mixins/post-ui.pug\n #recent-posts.recent-posts\n if theme.categoryBar.enable\n .recent-post-item(style='height:auto;width:100%;padding:0px;')\n #categoryBar!= list_categories(site.categories,{class: 'categoryBar',depth: 1})\n +postUI\n include includes/pagination.pug\n\n```\n\n(2)新建`[Blogroot]\\themes\\butterfly\\source\\css\\_layout\\categoryBar.styl`\n\n```\nif hexo-config('categoryBar.enable')\n #categoryBar\n width 100%!important\n ul\n &.categoryBar-list\n margin 5px 5px 0 5px!important\n padding 0!important\n\n li\n &.categoryBar-list-item\n font-weight bold\n display inline-block\n height 180px!important\n margin 5px .5% 0 .5%!important\n background-image linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(16, 16, 16, 0) 100%)\n border-radius 10px\n padding 25px 0 25px 25px!important\n box-shadow rgba(50, 50, 50, 0.3) 50px 50px 50px 50px inset\n overflow hidden\n background-size 100%!important\n background-position center!important\n &:hover\n background-size 110%!important\n box-shadow inset 500px 50px 50px 50px rgba(50,50,50, 0.6)\n span\n &.categoryBar-list-count\n &::after\n transition all .5s\n transform translate(-100%, 0)\n a\n &.categoryBar-list-link\n color white!important\n font-size 20px!important\n &::before\n content '|'!important\n color white!important\n font-size 20px!important\n &:after\n content ''\n position relative\n width 0\n bottom 0\n display block\n height 3px\n border-radius 3px\n background-color white\n &:hover\n &:after\n width 90%\n left 1%\n transition all 0.5s\n\n span\n &.categoryBar-list-count\n display block!important\n color white!important\n font-size 20px!important\n &::before\n content '\\f02d'!important\n padding-right 15px!important\n @extend .fontawesomeIcon\n &::after\n padding 5px\n display block!important\n color white!important\n font-size 20px!important\n position relative\n right -100%\n covers = hexo-config('categoryBar.cover')\n for cover,i in covers\n li.categoryBar-list-item:nth-child({i+1})\n background unquote(cover)\n descrs = hexo-config('categoryBar.descr')\n for descr,i in descrs\n li.categoryBar-list-item:nth-child({i+1})>span::after\n content descr!important\n if hexo-config('categoryBar.column') == 'odd'\n li\n &.categoryBar-list-item\n width 32.3%!important\n else if hexo-config('categoryBar.column') == 'even'\n li\n &.categoryBar-list-item\n width 24%!important\n @media screen and (max-width: 650px)\n li\n &.categoryBar-list-item\n width 48%!important\n height 150px!important\n margin 5px 1% 0 1%!important\n\n $caterow = hexo-config('categoryBar.row')?hexo-config('categoryBar.row'):2\n .categoryBar-list\n max-height 190px * $caterow\n overflow auto\n &::-webkit-scrollbar\n width 0!important\n @media screen and (max-width: 650px)\n .categoryBar-list\n max-height 160px * $caterow\n```\n\n(3)在`[Blogroot]\\_config.butterfly.yml`添加配置项:\n\n```yml\ncategoryBar:\n enable: true\n column: odd # 显示列数,odd:3列 | even:4列\n row: 1 #显示行数,默认两行,超过行数切换为滚动显示\n descr:\n - Hexo博客搭建记录\n - Java学习笔记\n - 电磁场课程学习笔记\n - 硬件学习过程记录\n cover:\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover1.webp')\n - '#abcdef' # HEX格式色值需要用''包裹,不然会被识别成注释\n - rgba(45,67,89,0.7)\n - linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(200,16 , 16, 0) 100%)\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover5.webp')\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover6.webp')\n\n```\n\n{%endfolding%}\n\n\n\n\n## 主页添加文章分类配置(与上一个样式不同)\n\n{%folding blue, 展开%}\n\n[前往小冰博客](https://zfe.space/post/hexo-magnet.html)\n\n效果如下:\n![磁体2.gif](https://cdn.nlark.com/yuque/0/2021/gif/8391485/1615908841586-e6737009-b2dd-4004-ae02-4c6d15f33701.gif)\n这个插件主要实现了以下功能:\n1.自定义 tags 或 categories 的排列和展示 \n2.自定义 tags 或 categories 的展示图标,名称 \n3.自定义排列的行数,默认 2 行\n\n**NPM 插件安装的部署方法**\n\n```powershell\nnpm i hexo-magnet --save\n\n# 或者\n\ncnpm i hexo-magnet --save\n```\n\n注意,一定要加`--save`,不然本地预览的时候可能不会显示!!!\n\n**新增网站根目录\\_config 配置项(不是主题的)**\n\n```yaml\nmagnet:\n enable: true\n priority: 1\n enable_page: /\n type: categories\n devide: 2\n display:\n - name: java\n display_name: aJreamのJava全栈学习\n icon: 📚\n - name: Hexo\n display_name: aJreamのHexo博客搭建&主题配置\n icon: 🎮\n - name: win10\n display_name: aJreamのWin10主题配置\n icon: 💻\n - name: 硬件学习\n display_name: aJreamの硬件学习\n icon: 🔧\n color_setting:\n text_color: black\n text_hover_color: white\n background_color: \"#f2f2f2\"\n background_hover_color: \"#aebfe3\"\n layout:\n type: id\n name: recent-posts\n index: 0\n temple_html: '
${temple_html_item}
'\n plus_style: \n```\n\n接下来来简单说明一下配置项的含义:\n\n> **enable_page**\n\n参数:/\n含义:路由地址,如 / 代表主页。/me/代表自我介绍页等等\n\n> **priority**\n\n参数:1\n含义:插件的叠放顺序,数字越大,叠放约靠前。如果你安装了 hexo-githubcalendar,请将`hexo-githubcalendar`npm 插件更新至`@1.2.3`版本。然后给 hexo-githubcalendar 添加`priority`参数。\n\n```yaml\ngithubcalendar:\n enable: true\n priority: 3 # 这里加上参数\n```\n\n> **type**\n\n参数:categories、tags\n含义:选择筛选分类还是标签\n\n> **devide**\n\n参数:2\n含义:表示分隔的列数,2 表示分为两列展示\n\n> **display**\n\n参数:\n\n```yaml\n- name: 教程 # 这里是tags或者categories的名称\n display_name: 小冰の魔改教程 # 这里是替换的名称\n icon: 📚 # 这里是展示的图标\n```\n\n含义:配置项,可自行设置,按照设置的顺序展示\n\n> **color_setting**\n\n参数:\n\n```yaml\ntext_color: black # 文字默认颜色\ntext_hover_color: white # 文字鼠标悬浮颜色\nbackground_color: \"#f2f2f2\" # 文字背景默认颜色\nbackground_hover_color: \"#b30070\" # 文字背景悬浮颜色\n```\n\n含义:颜色配置项,可自行设置\n\n> **layout**\n\n参数:type; (class&id)\n参数:name;\n参数:index;(数字)\n含义:如果说 gihubcalendar 是一幅画,那么这个 layout 就是指定了哪面墙来挂画\n而在 HTML 的是世界里有两种墙分别 type 为 id 和 class。\n其中在定义 class 的时候会出现多个 class 的情况,这时就需要使用 index,确定是哪一个。\n最后墙的名字即是 name;\n\n```html\n
\n \n
\n
\n \n
\n
\n
\n```\n\n> **temple_html**\n\n参数:html 模板字段\n含义:包含挂载容器\n\n```xml\n
\n
\n ${temple_html_item}\n
\n
\n```\n\n> **plus_style**\n\n参数:\"\"\n含义:提供可自定义的 style,如加入黑夜模式,也可以通过CSS来添加。\n{%endfolding%}\n\n### 添加黑夜模式\n\n在 `custom.css` 文件中添加以下代码,使其在黑夜模式下生效\n```css\n[data-theme=\"dark\"] .magnet_link_context {\n background: #201e1e;\n border-radius: 6px;\n color: white;\n \n}\n\n[data-theme=\"dark\"] .magnet_link_context:hover {\n background: #3b4042;\n}\n```\n修改各类的跳转链接:\n原本点击各个分类后,默认跳转的链接是 `_config.yml` 文件中配置的 url值 + \"/categories\" + 【各个分类】\n\n但为了能使部署在不同服务器上能够跳转到自己的分类下,修改了插件`/node_modules/hexo-magnet/index.js`的代码:\n将第61行(或者在附近某行)的代码 `href=\"${hexo.config.url}/${item.path}\"` 的 `${hexo.config.url}`删掉 ,修改后如下:\n\n```js\ntemple_html_item += ``;\n```\n\n\n### hexo 三连\n\n执行 hexo 三连\n\n```powershell\nhexo clean && hexo g && hexo s\n```\n\n即可发现已经成功部署。\n\n\n\n\n## 标题h1~h6美化\n\n[参考大佬](https://guole.fun/posts/butterfly-custom/#%E6%96%87%E7%AB%A0%E9%A1%B5H1-H6%E6%A0%B7%E5%BC%8F%E4%BF%AE%E6%94%B9)\n\n{%folding blue, 展开%}\nButterfly 在 H1~H6 样式上使用了 [fontawesome.com](https://fontawesome.com/v5.15/icons?from=io)上的图标,引用的是 Unicode 形式。可自行查找合适的。\n![image-20210908221739125](https://gitee.com/ajream/images/raw/master/img/20210908221743_image-20210908221739125.png)\n\n{%tabs 小风车, -1%}\n\n本站小风车样式\n\n```yml\nbeautify:\n enable: true\n field: post # site/post\n # title-prefix-icon: '\\f0c1' 原内容\n title-prefix-icon: '\\f185'\n title-prefix-icon-color: '#F47466'\n```\n\n\n\n\n\n让小风车转起来\n\n在 `/css/custom.css` 文件中,加入以下代码即可。\n\n转速、转向修改看注释\n\n\n```css\n/* 文章页H1-H6图标样式效果 */\nh1::before,\nh2::before,\nh3::before,\nh4::before,\nh5::before,\nh6::before {\n -webkit-animation: ccc 1.6s linear infinite;\n animation: ccc 1.6s linear infinite;\n /* 转速:1.6,数字越小转速越快 */\n}\n\n@-webkit-keyframes ccc {\n \n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg)\n }\n/* -1turn 为逆时针转动,1turn 为顺时针转动,相同数字部分记得统一修改: */\n to {\n -webkit-transform: rotate(-1turn);\n transform: rotate(-1turn)\n }\n}\n\n@keyframes ccc {\n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg)\n }\n\n to {\n -webkit-transform: rotate(-1turn);\n transform: rotate(-1turn)\n }\n}\n\n```\n\n\n\n\n\n小风车颜色、大小修改\n\n直接上代码\n\n```css\n#content-inner.layout h1::before {\n color: #ef50a8 ;\n margin-left: -1.55rem;\n font-size: 1.3rem;\n margin-top: -0.23rem;\n}\n#content-inner.layout h2::before {\n color: #fb7061 ;\n margin-left: -1.35rem;\n font-size: 1.1rem;\n margin-top: -0.12rem;\n}\n#content-inner.layout h3::before {\n color: #ffbf00 ;\n margin-left: -1.22rem;\n font-size: 0.95rem;\n margin-top: -0.09rem;\n}\n#content-inner.layout h4::before {\n color: #a9e000 ;\n margin-left: -1.05rem;\n font-size: 0.8rem;\n margin-top: -0.09rem;\n}\n#content-inner.layout h5::before {\n color: #57c850 ;\n margin-left: -0.9rem;\n font-size: 0.7rem;\n margin-top: 0.0rem;\n}\n#content-inner.layout h6::before {\n color: #5ec1e0 ;\n margin-left: -0.9rem;\n font-size: 0.66rem;\n margin-top: 0.0rem;\n}\n\n```\n\n\n\n\n小风车hover效果\n\n鼠标碰到小风车转速变慢及变色\n\n设置鼠标碰到标题时,小风车跟随标题变色,且像是被光标阻碍了,转速变慢。鼠标离开恢复转速。也可以设置为 none 鼠标碰到停止转动。\n\n```css\n\n#content-inner.layout h1:hover, #content-inner.layout h2:hover, #content-inner.layout h3:hover, #content-inner.layout h4:hover, #content-inner.layout h5:hover, #content-inner.layout h6:hover {\n color: #49b1f5 ;\n}\n#content-inner.layout h1:hover::before, #content-inner.layout h2:hover::before, #content-inner.layout h3:hover::before, #content-inner.layout h4:hover::before, #content-inner.layout h5:hover::before, #content-inner.layout h6:hover::before {\n color: #49b1f5 ;\n -webkit-animation: ccc 3.2s linear infinite ;\n animation: ccc 3.2s linear infinite ;\n}\n\n```\n\n\n\n{%endtabs%}\n\n{%endfolding%}","slug":"Hexo/butterfly主题配置","published":1,"updated":"2021-09-16T15:28:56.062Z","_id":"cktk8o6tu00h1akve7d054dj8","comments":1,"layout":"post","photos":[],"link":"","content":"

下载安装

\n
参考店长文章 \n \n
\n

页面主题颜色配置

\n
配置 \n
\n

在主题配置文件_config.butterfly.yml修改即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
theme_color:
enable: true
main: "#49B1F5"
paginator: "#00c4b6"
button_hover: "#FF7242"
text_selection: "#00c4b6"
link_color: "#99a9bf"
meta_color: "#858585"
hr_color: "#A4D8FA"
code_foreground: "#F47466"
code_background: "rgba(27, 31, 35, .05)"
toc_color: "#00c4b6"
blockquote_padding_color: "#49b1f5"
blockquote_background_color: "#49b1f5"

\n
\n
\n

外挂标签

\n

参考小康博客

\n
查看 \n \n
\n

全局背景透明渐变

\n

小康博客

\n

首页置顶轮播图

\n

糖果屋教程

\n
步骤 \n
\n
  1. 安装插件

    1
    npm install hexo-butterfly-swiper --save
  2. 在站点文件 _config.yml添加如下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # hexo-butterfly-swiper
    # see https://akilar.top/posts/8e1264d1/
    swiper:
    enable: true # 开关
    priority: 5 #过滤器优先权
    enable_page: all # 应用页面
    timemode: date #date/updated
    layout: # 挂载容器类型
    type: id
    name: recent-posts
    index: 0
    default_descr: 再怎么看我也不知道怎么描述它的啦!
    custom_css: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/css/swiper.min.css #自定义swiper css依赖
    custom_js: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/js/swiper.min.js #自定义swiper js依赖
参数备选值/类型释义
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为10,选填
enabletrue/false【必选】控制开关
enable_pagepath/all【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为all
timemodedate/updated【可选】时间显示,date为显示创建日期,updated为显示更新日期,默认为date
layout.typeid/class【可选】挂载容器类型,填写id或class,不填则默认为id
layout.nametext【必选】挂载容器名称
layout.index0和正整数【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位
default_descrtext默认文章描述
custom_cssurl【可选】自定义的swiper依赖项css链接
custom_jsurl【可选】自定义的swiper依赖项加js链接
  1. 在文章的 front_matter 添加 swiper_index 配置项即可

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    title: 文章标题
    date: 创建日期
    updated: 更新日期
    cover: 文章封面
    description: 文章描述
    swiper_index: 1 #置顶轮播图顺序,非负整数,数字越大越靠前
    ---
\n
\n
\n

首页卡片浮动显示wowjs

\n
插件版本 \n
\n

糖果屋教程

  1. 安装插件

    1
    npm install hexo-butterfly-wowjs --save
  2. 添加配置信息,以下为写法示例
    在站点配置文件_config.yml 或者主题配置文件_config.butterfly.yml 中添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    wowjs:
    enable: true #控制动画开关。true是打开,false是关闭
    priority: 10 #过滤器优先级
    mobile: false #移动端是否启用,默认移动端禁用
    animateitem:
    - class: recent-post-item #必填项,需要添加动画的元素的class
    style: animate__zoomIn #必填项,需要添加的动画
    duration: 2s #选填项,动画持续时间,单位可以是ms也可以是s。例如3s,700ms。
    delay: 1s #选填项,动画开始的延迟时间,单位可以是ms也可以是s。例如3s,700ms。
    offset: 100 #选填项,开始动画的距离(相对浏览器底部)
    iteration: 2 #选填项,动画重复的次数
    - class: card-widget
    style: animate__zoomIn
参数备选值 / 类型释义
enabletrue/false【必选】控制开关
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为 10,选填
mobiletrue/false控制移动端是否启用,默认移动端禁用
animateitem.classclass【可选】添加动画类名,只支持给 class 添加
animateitem.styletext【可选】动画样式,具体类型参考 animate.css
animateitem.durationtime, 单位为 s 或 ms【可选】动画持续时间,单位可以是 ms 也可以是 s。例如 3s,700ms。
animateitem.delaytime, 单位为 s 或 ms【可选】动画开始的延迟时间,单位可以是 ms 也可以是 s。例如 3s,700ms。
animateitem.offsetnumber, 单位为 px【可选】开始动画的距离(相对浏览器底部)。
animateitem.iterationnumber, 单位为 s 或 ms【可选】动画重复的次数
\n
\n
\n

看板娘配置

\n

糖果屋教程

\n

看板娘不能换装,参考糖果屋的解决方法:

\n
点击展开 \n
\n

使用糖果屋大佬配置好的本地化项目的路径:
修改项目内的 ~\\live2d-widget\\autoload.js, 修改 cdnPath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  // 加载 waifu.css live2d.min.js waifu-tips.js
if (screen.width >= 768) {
\tPromise.all([
\t\tloadExternalResource(live2d_path + "waifu.css", "css"),
\t\tloadExternalResource(live2d_path + "live2d.min.js", "js"),
\t\tloadExternalResource(live2d_path + "waifu-tips.js", "js")
\t]).then(() => {
\t\tinitWidget({
\t\t\twaifuPath: live2d_path + "waifu-tips.json",
\t\t\t//apiPath: "https://live2d.fghrsh.net/api/",
- \t\t\tcdnPath: "https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/"
+ \t\t\tcdnPath: "https://live2d-api.vercel.app/"
//因为jsdelivr不支持50MB以上的包的加速,可能报403错误,所以用的vercel的CDN服务。
//可以考虑clone大佬配置好的live2d_api仓库自己部署到其他更快的cdn服务上。
\t\t});
\t});
}

\n
\n
\n

主页分类磁铁配置

\n

糖果屋教程

\n
点击展开 \n
\n

(1)修改[Blogroot]\\themes\\butterfly\\layout\\index.pug, 添加6~8行代码,以下是添加后的结果(注意代码格式比较严格,可直接复制)

1
2
3
4
5
6
7
8
9
10
11
extends includes/layout.pug

block content
include ./includes/mixins/post-ui.pug
#recent-posts.recent-posts
if theme.categoryBar.enable
.recent-post-item(style='height:auto;width:100%;padding:0px;')
#categoryBar!= list_categories(site.categories,{class: 'categoryBar',depth: 1})
+postUI
include includes/pagination.pug

(2)新建[Blogroot]\\themes\\butterfly\\source\\css\\_layout\\categoryBar.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
if hexo-config('categoryBar.enable')
#categoryBar
width 100%!important
ul
&.categoryBar-list
margin 5px 5px 0 5px!important
padding 0!important

li
&.categoryBar-list-item
font-weight bold
display inline-block
height 180px!important
margin 5px .5% 0 .5%!important
background-image linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(16, 16, 16, 0) 100%)
border-radius 10px
padding 25px 0 25px 25px!important
box-shadow rgba(50, 50, 50, 0.3) 50px 50px 50px 50px inset
overflow hidden
background-size 100%!important
background-position center!important
&:hover
background-size 110%!important
box-shadow inset 500px 50px 50px 50px rgba(50,50,50, 0.6)
span
&.categoryBar-list-count
&::after
transition all .5s
transform translate(-100%, 0)
a
&.categoryBar-list-link
color white!important
font-size 20px!important
&::before
content '|'!important
color white!important
font-size 20px!important
&:after
content ''
position relative
width 0
bottom 0
display block
height 3px
border-radius 3px
background-color white
&:hover
&:after
width 90%
left 1%
transition all 0.5s

span
&.categoryBar-list-count
display block!important
color white!important
font-size 20px!important
&::before
content '\\f02d'!important
padding-right 15px!important
@extend .fontawesomeIcon
&::after
padding 5px
display block!important
color white!important
font-size 20px!important
position relative
right -100%
covers = hexo-config('categoryBar.cover')
for cover,i in covers
li.categoryBar-list-item:nth-child({i+1})
background unquote(cover)
descrs = hexo-config('categoryBar.descr')
for descr,i in descrs
li.categoryBar-list-item:nth-child({i+1})>span::after
content descr!important
if hexo-config('categoryBar.column') == 'odd'
li
&.categoryBar-list-item
width 32.3%!important
else if hexo-config('categoryBar.column') == 'even'
li
&.categoryBar-list-item
width 24%!important
@media screen and (max-width: 650px)
li
&.categoryBar-list-item
width 48%!important
height 150px!important
margin 5px 1% 0 1%!important

$caterow = hexo-config('categoryBar.row')?hexo-config('categoryBar.row'):2
.categoryBar-list
max-height 190px * $caterow
overflow auto
&::-webkit-scrollbar
width 0!important
@media screen and (max-width: 650px)
.categoryBar-list
max-height 160px * $caterow

(3)在[Blogroot]\\_config.butterfly.yml添加配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
categoryBar:
enable: true
column: odd # 显示列数,odd:3列 | even:4列
row: 1 #显示行数,默认两行,超过行数切换为滚动显示
descr:
- Hexo博客搭建记录
- Java学习笔记
- 电磁场课程学习笔记
- 硬件学习过程记录
cover:
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover1.webp')
- '#abcdef' # HEX格式色值需要用''包裹,不然会被识别成注释
- rgba(45,67,89,0.7)
- linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(200,16 , 16, 0) 100%)
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover5.webp')
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover6.webp')

\n
\n
\n

主页添加文章分类配置(与上一个样式不同)

\n
展开 \n
\n

前往小冰博客

效果如下:
\"磁体2.gif\"
这个插件主要实现了以下功能:
1.自定义 tags 或 categories 的排列和展示
2.自定义 tags 或 categories 的展示图标,名称
3.自定义排列的行数,默认 2 行

NPM 插件安装的部署方法

1
2
3
4
5
npm i hexo-magnet --save

# 或者

cnpm i hexo-magnet --save

注意,一定要加--save,不然本地预览的时候可能不会显示!!!

新增网站根目录_config 配置项(不是主题的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
magnet:
enable: true
priority: 1
enable_page: /
type: categories
devide: 2
display:
- name: java
display_name: aJreamのJava全栈学习
icon: 📚
- name: Hexo
display_name: aJreamのHexo博客搭建&主题配置
icon: 🎮
- name: win10
display_name: aJreamのWin10主题配置
icon: 💻
- name: 硬件学习
display_name: aJreamの硬件学习
icon: 🔧
color_setting:
text_color: black
text_hover_color: white
background_color: "#f2f2f2"
background_hover_color: "#aebfe3"
layout:
type: id
name: recent-posts
index: 0
temple_html: '<div class="recent-post-item" style="width:100%;height: auto"><div id="catalog_magnet">${temple_html_item}</div></div>'
plus_style:

接下来来简单说明一下配置项的含义:

enable_page

参数:/
含义:路由地址,如 / 代表主页。/me/代表自我介绍页等等

priority

参数:1
含义:插件的叠放顺序,数字越大,叠放约靠前。如果你安装了 hexo-githubcalendar,请将hexo-githubcalendarnpm 插件更新至@1.2.3版本。然后给 hexo-githubcalendar 添加priority参数。

1
2
3
githubcalendar:
enable: true
priority: 3 # 这里加上参数

type

参数:categories、tags
含义:选择筛选分类还是标签

devide

参数:2
含义:表示分隔的列数,2 表示分为两列展示

display

参数:

1
2
3
- name: 教程 # 这里是tags或者categories的名称
display_name: 小冰の魔改教程 # 这里是替换的名称
icon: 📚 # 这里是展示的图标

含义:配置项,可自行设置,按照设置的顺序展示

color_setting

参数:

1
2
3
4
text_color: black # 文字默认颜色
text_hover_color: white # 文字鼠标悬浮颜色
background_color: "#f2f2f2" # 文字背景默认颜色
background_hover_color: "#b30070" # 文字背景悬浮颜色

含义:颜色配置项,可自行设置

layout

参数:type; (class&id)
参数:name;
参数:index;(数字)
含义:如果说 gihubcalendar 是一幅画,那么这个 layout 就是指定了哪面墙来挂画
而在 HTML 的是世界里有两种墙分别 type 为 id 和 class。
其中在定义 class 的时候会出现多个 class 的情况,这时就需要使用 index,确定是哪一个。
最后墙的名字即是 name;

1
2
3
4
5
6
7
8
<div name="我是墙" id="recent-posts">
<!-- id=>type recent-posts=>name -->
<div name="我是画框">
<div name="我是纸">
<!--这里通过js挂载githubcalendar,也就是画画-->
</div>
</div>
</div>

temple_html

参数:html 模板字段
含义:包含挂载容器

1
2
3
4
5
<div class="recent-post-item" style="width:100%;height: auto"> <!--文章容器-->
<div id="catalog_magnet"> <!--挂载容器-->
${temple_html_item}
</div>
</div>

plus_style

参数:“”
含义:提供可自定义的 style,如加入黑夜模式,也可以通过CSS来添加。

\n
\n
\n

添加黑夜模式

\n

custom.css 文件中添加以下代码,使其在黑夜模式下生效

\n
1
2
3
4
5
6
7
8
9
10
[data-theme="dark"] .magnet_link_context {
background: #201e1e;
border-radius: 6px;
color: white;

}

[data-theme="dark"] .magnet_link_context:hover {
background: #3b4042;
}
\n

修改各类的跳转链接:
\n原本点击各个分类后,默认跳转的链接是 _config.yml 文件中配置的 url值 + “/categories” + 【各个分类】

\n

但为了能使部署在不同服务器上能够跳转到自己的分类下,修改了插件/node_modules/hexo-magnet/index.js的代码:
\n将第61行(或者在附近某行)的代码 href="${hexo.config.url}/${item.path}"${hexo.config.url}删掉 ,修改后如下:

\n
1
temple_html_item += `<div class="magnet_item"><a class="magnet_link" href="/${item.path}"><div class="magnet_link_context" style=""><span style="font-weight:500;flex:1">${j.icon} ${j.display_name}${br_devide}(${item.length})</span><span style="padding:0px 4px;border-radius: 8px;"><i class="fas fa-arrow-circle-right"></i></span></div></a></div>`;
\n

hexo 三连

\n

执行 hexo 三连

\n
1
hexo clean && hexo g && hexo s
\n

即可发现已经成功部署。

\n

标题h1~h6美化

\n

参考大佬

\n
展开 \n
\n

Butterfly 在 H1~H6 样式上使用了 fontawesome.com上的图标,引用的是 Unicode 形式。可自行查找合适的。
\"image-20210908221739125\"

本站小风车样式

1
2
3
4
5
6
beautify:
enable: true
field: post # site/post
# title-prefix-icon: '\\f0c1' 原内容
title-prefix-icon: '\\f185'
title-prefix-icon-color: '#F47466'

让小风车转起来

/css/custom.css 文件中,加入以下代码即可。

转速、转向修改看注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* 文章页H1-H6图标样式效果 */
h1::before,
h2::before,
h3::before,
h4::before,
h5::before,
h6::before {
-webkit-animation: ccc 1.6s linear infinite;
animation: ccc 1.6s linear infinite;
/* 转速:1.6,数字越小转速越快 */
}

@-webkit-keyframes ccc {

0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
/* -1turn 为逆时针转动,1turn 为顺时针转动,相同数字部分记得统一修改: */
to {
-webkit-transform: rotate(-1turn);
transform: rotate(-1turn)
}
}

@keyframes ccc {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}

to {
-webkit-transform: rotate(-1turn);
transform: rotate(-1turn)
}
}

小风车颜色、大小修改

直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#content-inner.layout h1::before {
color: #ef50a8 ;
margin-left: -1.55rem;
font-size: 1.3rem;
margin-top: -0.23rem;
}
#content-inner.layout h2::before {
color: #fb7061 ;
margin-left: -1.35rem;
font-size: 1.1rem;
margin-top: -0.12rem;
}
#content-inner.layout h3::before {
color: #ffbf00 ;
margin-left: -1.22rem;
font-size: 0.95rem;
margin-top: -0.09rem;
}
#content-inner.layout h4::before {
color: #a9e000 ;
margin-left: -1.05rem;
font-size: 0.8rem;
margin-top: -0.09rem;
}
#content-inner.layout h5::before {
color: #57c850 ;
margin-left: -0.9rem;
font-size: 0.7rem;
margin-top: 0.0rem;
}
#content-inner.layout h6::before {
color: #5ec1e0 ;
margin-left: -0.9rem;
font-size: 0.66rem;
margin-top: 0.0rem;
}

小风车hover效果

鼠标碰到小风车转速变慢及变色

设置鼠标碰到标题时,小风车跟随标题变色,且像是被光标阻碍了,转速变慢。鼠标离开恢复转速。也可以设置为 none 鼠标碰到停止转动。

1
2
3
4
5
6
7
8
9
10

#content-inner.layout h1:hover, #content-inner.layout h2:hover, #content-inner.layout h3:hover, #content-inner.layout h4:hover, #content-inner.layout h5:hover, #content-inner.layout h6:hover {
color: #49b1f5 ;
}
#content-inner.layout h1:hover::before, #content-inner.layout h2:hover::before, #content-inner.layout h3:hover::before, #content-inner.layout h4:hover::before, #content-inner.layout h5:hover::before, #content-inner.layout h6:hover::before {
color: #49b1f5 ;
-webkit-animation: ccc 3.2s linear infinite ;
animation: ccc 3.2s linear infinite ;
}

\n
\n
","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

下载安装

\n
参考店长文章 \n \n
\n

页面主题颜色配置

\n
配置 \n
\n

在主题配置文件_config.butterfly.yml修改即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
theme_color:
enable: true
main: "#49B1F5"
paginator: "#00c4b6"
button_hover: "#FF7242"
text_selection: "#00c4b6"
link_color: "#99a9bf"
meta_color: "#858585"
hr_color: "#A4D8FA"
code_foreground: "#F47466"
code_background: "rgba(27, 31, 35, .05)"
toc_color: "#00c4b6"
blockquote_padding_color: "#49b1f5"
blockquote_background_color: "#49b1f5"

\n
\n
\n

外挂标签

\n

参考小康博客

\n
查看 \n \n
\n

全局背景透明渐变

\n

小康博客

\n

首页置顶轮播图

\n

糖果屋教程

\n
步骤 \n
\n
  1. 安装插件

    1
    npm install hexo-butterfly-swiper --save
  2. 在站点文件 _config.yml添加如下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # hexo-butterfly-swiper
    # see https://akilar.top/posts/8e1264d1/
    swiper:
    enable: true # 开关
    priority: 5 #过滤器优先权
    enable_page: all # 应用页面
    timemode: date #date/updated
    layout: # 挂载容器类型
    type: id
    name: recent-posts
    index: 0
    default_descr: 再怎么看我也不知道怎么描述它的啦!
    custom_css: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/css/swiper.min.css #自定义swiper css依赖
    custom_js: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/js/swiper.min.js #自定义swiper js依赖
参数备选值/类型释义
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为10,选填
enabletrue/false【必选】控制开关
enable_pagepath/all【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为all
timemodedate/updated【可选】时间显示,date为显示创建日期,updated为显示更新日期,默认为date
layout.typeid/class【可选】挂载容器类型,填写id或class,不填则默认为id
layout.nametext【必选】挂载容器名称
layout.index0和正整数【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位
default_descrtext默认文章描述
custom_cssurl【可选】自定义的swiper依赖项css链接
custom_jsurl【可选】自定义的swiper依赖项加js链接
  1. 在文章的 front_matter 添加 swiper_index 配置项即可

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    title: 文章标题
    date: 创建日期
    updated: 更新日期
    cover: 文章封面
    description: 文章描述
    swiper_index: 1 #置顶轮播图顺序,非负整数,数字越大越靠前
    ---
\n
\n
\n

首页卡片浮动显示wowjs

\n
插件版本 \n
\n

糖果屋教程

  1. 安装插件

    1
    npm install hexo-butterfly-wowjs --save
  2. 添加配置信息,以下为写法示例
    在站点配置文件_config.yml 或者主题配置文件_config.butterfly.yml 中添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    wowjs:
    enable: true #控制动画开关。true是打开,false是关闭
    priority: 10 #过滤器优先级
    mobile: false #移动端是否启用,默认移动端禁用
    animateitem:
    - class: recent-post-item #必填项,需要添加动画的元素的class
    style: animate__zoomIn #必填项,需要添加的动画
    duration: 2s #选填项,动画持续时间,单位可以是ms也可以是s。例如3s,700ms。
    delay: 1s #选填项,动画开始的延迟时间,单位可以是ms也可以是s。例如3s,700ms。
    offset: 100 #选填项,开始动画的距离(相对浏览器底部)
    iteration: 2 #选填项,动画重复的次数
    - class: card-widget
    style: animate__zoomIn
参数备选值 / 类型释义
enabletrue/false【必选】控制开关
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为 10,选填
mobiletrue/false控制移动端是否启用,默认移动端禁用
animateitem.classclass【可选】添加动画类名,只支持给 class 添加
animateitem.styletext【可选】动画样式,具体类型参考 animate.css
animateitem.durationtime, 单位为 s 或 ms【可选】动画持续时间,单位可以是 ms 也可以是 s。例如 3s,700ms。
animateitem.delaytime, 单位为 s 或 ms【可选】动画开始的延迟时间,单位可以是 ms 也可以是 s。例如 3s,700ms。
animateitem.offsetnumber, 单位为 px【可选】开始动画的距离(相对浏览器底部)。
animateitem.iterationnumber, 单位为 s 或 ms【可选】动画重复的次数
\n
\n
\n

看板娘配置

\n

糖果屋教程

\n

看板娘不能换装,参考糖果屋的解决方法:

\n
点击展开 \n
\n

使用糖果屋大佬配置好的本地化项目的路径:
修改项目内的 ~\\live2d-widget\\autoload.js, 修改 cdnPath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  // 加载 waifu.css live2d.min.js waifu-tips.js
if (screen.width >= 768) {
\tPromise.all([
\t\tloadExternalResource(live2d_path + "waifu.css", "css"),
\t\tloadExternalResource(live2d_path + "live2d.min.js", "js"),
\t\tloadExternalResource(live2d_path + "waifu-tips.js", "js")
\t]).then(() => {
\t\tinitWidget({
\t\t\twaifuPath: live2d_path + "waifu-tips.json",
\t\t\t//apiPath: "https://live2d.fghrsh.net/api/",
- \t\t\tcdnPath: "https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/"
+ \t\t\tcdnPath: "https://live2d-api.vercel.app/"
//因为jsdelivr不支持50MB以上的包的加速,可能报403错误,所以用的vercel的CDN服务。
//可以考虑clone大佬配置好的live2d_api仓库自己部署到其他更快的cdn服务上。
\t\t});
\t});
}

\n
\n
\n

主页分类磁铁配置

\n

糖果屋教程

\n
点击展开 \n
\n

(1)修改[Blogroot]\\themes\\butterfly\\layout\\index.pug, 添加6~8行代码,以下是添加后的结果(注意代码格式比较严格,可直接复制)

1
2
3
4
5
6
7
8
9
10
11
extends includes/layout.pug

block content
include ./includes/mixins/post-ui.pug
#recent-posts.recent-posts
if theme.categoryBar.enable
.recent-post-item(style='height:auto;width:100%;padding:0px;')
#categoryBar!= list_categories(site.categories,{class: 'categoryBar',depth: 1})
+postUI
include includes/pagination.pug

(2)新建[Blogroot]\\themes\\butterfly\\source\\css\\_layout\\categoryBar.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
if hexo-config('categoryBar.enable')
#categoryBar
width 100%!important
ul
&.categoryBar-list
margin 5px 5px 0 5px!important
padding 0!important

li
&.categoryBar-list-item
font-weight bold
display inline-block
height 180px!important
margin 5px .5% 0 .5%!important
background-image linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(16, 16, 16, 0) 100%)
border-radius 10px
padding 25px 0 25px 25px!important
box-shadow rgba(50, 50, 50, 0.3) 50px 50px 50px 50px inset
overflow hidden
background-size 100%!important
background-position center!important
&:hover
background-size 110%!important
box-shadow inset 500px 50px 50px 50px rgba(50,50,50, 0.6)
span
&.categoryBar-list-count
&::after
transition all .5s
transform translate(-100%, 0)
a
&.categoryBar-list-link
color white!important
font-size 20px!important
&::before
content '|'!important
color white!important
font-size 20px!important
&:after
content ''
position relative
width 0
bottom 0
display block
height 3px
border-radius 3px
background-color white
&:hover
&:after
width 90%
left 1%
transition all 0.5s

span
&.categoryBar-list-count
display block!important
color white!important
font-size 20px!important
&::before
content '\\f02d'!important
padding-right 15px!important
@extend .fontawesomeIcon
&::after
padding 5px
display block!important
color white!important
font-size 20px!important
position relative
right -100%
covers = hexo-config('categoryBar.cover')
for cover,i in covers
li.categoryBar-list-item:nth-child({i+1})
background unquote(cover)
descrs = hexo-config('categoryBar.descr')
for descr,i in descrs
li.categoryBar-list-item:nth-child({i+1})>span::after
content descr!important
if hexo-config('categoryBar.column') == 'odd'
li
&.categoryBar-list-item
width 32.3%!important
else if hexo-config('categoryBar.column') == 'even'
li
&.categoryBar-list-item
width 24%!important
@media screen and (max-width: 650px)
li
&.categoryBar-list-item
width 48%!important
height 150px!important
margin 5px 1% 0 1%!important

$caterow = hexo-config('categoryBar.row')?hexo-config('categoryBar.row'):2
.categoryBar-list
max-height 190px * $caterow
overflow auto
&::-webkit-scrollbar
width 0!important
@media screen and (max-width: 650px)
.categoryBar-list
max-height 160px * $caterow

(3)在[Blogroot]\\_config.butterfly.yml添加配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
categoryBar:
enable: true
column: odd # 显示列数,odd:3列 | even:4列
row: 1 #显示行数,默认两行,超过行数切换为滚动显示
descr:
- Hexo博客搭建记录
- Java学习笔记
- 电磁场课程学习笔记
- 硬件学习过程记录
cover:
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover1.webp')
- '#abcdef' # HEX格式色值需要用''包裹,不然会被识别成注释
- rgba(45,67,89,0.7)
- linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(200,16 , 16, 0) 100%)
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover5.webp')
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover6.webp')

\n
\n
\n

主页添加文章分类配置(与上一个样式不同)

\n
展开 \n
\n

前往小冰博客

效果如下:
\"磁体2.gif\"
这个插件主要实现了以下功能:
1.自定义 tags 或 categories 的排列和展示
2.自定义 tags 或 categories 的展示图标,名称
3.自定义排列的行数,默认 2 行

NPM 插件安装的部署方法

1
2
3
4
5
npm i hexo-magnet --save

# 或者

cnpm i hexo-magnet --save

注意,一定要加--save,不然本地预览的时候可能不会显示!!!

新增网站根目录_config 配置项(不是主题的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
magnet:
enable: true
priority: 1
enable_page: /
type: categories
devide: 2
display:
- name: java
display_name: aJreamのJava全栈学习
icon: 📚
- name: Hexo
display_name: aJreamのHexo博客搭建&主题配置
icon: 🎮
- name: win10
display_name: aJreamのWin10主题配置
icon: 💻
- name: 硬件学习
display_name: aJreamの硬件学习
icon: 🔧
color_setting:
text_color: black
text_hover_color: white
background_color: "#f2f2f2"
background_hover_color: "#aebfe3"
layout:
type: id
name: recent-posts
index: 0
temple_html: '<div class="recent-post-item" style="width:100%;height: auto"><div id="catalog_magnet">${temple_html_item}</div></div>'
plus_style:

接下来来简单说明一下配置项的含义:

enable_page

参数:/
含义:路由地址,如 / 代表主页。/me/代表自我介绍页等等

priority

参数:1
含义:插件的叠放顺序,数字越大,叠放约靠前。如果你安装了 hexo-githubcalendar,请将hexo-githubcalendarnpm 插件更新至@1.2.3版本。然后给 hexo-githubcalendar 添加priority参数。

1
2
3
githubcalendar:
enable: true
priority: 3 # 这里加上参数

type

参数:categories、tags
含义:选择筛选分类还是标签

devide

参数:2
含义:表示分隔的列数,2 表示分为两列展示

display

参数:

1
2
3
- name: 教程 # 这里是tags或者categories的名称
display_name: 小冰の魔改教程 # 这里是替换的名称
icon: 📚 # 这里是展示的图标

含义:配置项,可自行设置,按照设置的顺序展示

color_setting

参数:

1
2
3
4
text_color: black # 文字默认颜色
text_hover_color: white # 文字鼠标悬浮颜色
background_color: "#f2f2f2" # 文字背景默认颜色
background_hover_color: "#b30070" # 文字背景悬浮颜色

含义:颜色配置项,可自行设置

layout

参数:type; (class&id)
参数:name;
参数:index;(数字)
含义:如果说 gihubcalendar 是一幅画,那么这个 layout 就是指定了哪面墙来挂画
而在 HTML 的是世界里有两种墙分别 type 为 id 和 class。
其中在定义 class 的时候会出现多个 class 的情况,这时就需要使用 index,确定是哪一个。
最后墙的名字即是 name;

1
2
3
4
5
6
7
8
<div name="我是墙" id="recent-posts">
<!-- id=>type recent-posts=>name -->
<div name="我是画框">
<div name="我是纸">
<!--这里通过js挂载githubcalendar,也就是画画-->
</div>
</div>
</div>

temple_html

参数:html 模板字段
含义:包含挂载容器

1
2
3
4
5
<div class="recent-post-item" style="width:100%;height: auto"> <!--文章容器-->
<div id="catalog_magnet"> <!--挂载容器-->
${temple_html_item}
</div>
</div>

plus_style

参数:“”
含义:提供可自定义的 style,如加入黑夜模式,也可以通过CSS来添加。

\n
\n
\n

添加黑夜模式

\n

custom.css 文件中添加以下代码,使其在黑夜模式下生效

\n
1
2
3
4
5
6
7
8
9
10
[data-theme="dark"] .magnet_link_context {
background: #201e1e;
border-radius: 6px;
color: white;

}

[data-theme="dark"] .magnet_link_context:hover {
background: #3b4042;
}
\n

修改各类的跳转链接:
\n原本点击各个分类后,默认跳转的链接是 _config.yml 文件中配置的 url值 + “/categories” + 【各个分类】

\n

但为了能使部署在不同服务器上能够跳转到自己的分类下,修改了插件/node_modules/hexo-magnet/index.js的代码:
\n将第61行(或者在附近某行)的代码 href="${hexo.config.url}/${item.path}"${hexo.config.url}删掉 ,修改后如下:

\n
1
temple_html_item += `<div class="magnet_item"><a class="magnet_link" href="/${item.path}"><div class="magnet_link_context" style=""><span style="font-weight:500;flex:1">${j.icon} ${j.display_name}${br_devide}(${item.length})</span><span style="padding:0px 4px;border-radius: 8px;"><i class="fas fa-arrow-circle-right"></i></span></div></a></div>`;
\n

hexo 三连

\n

执行 hexo 三连

\n
1
hexo clean && hexo g && hexo s
\n

即可发现已经成功部署。

\n

标题h1~h6美化

\n

参考大佬

\n
展开 \n
\n

Butterfly 在 H1~H6 样式上使用了 fontawesome.com上的图标,引用的是 Unicode 形式。可自行查找合适的。
\"image-20210908221739125\"

本站小风车样式

1
2
3
4
5
6
beautify:
enable: true
field: post # site/post
# title-prefix-icon: '\\f0c1' 原内容
title-prefix-icon: '\\f185'
title-prefix-icon-color: '#F47466'

让小风车转起来

/css/custom.css 文件中,加入以下代码即可。

转速、转向修改看注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* 文章页H1-H6图标样式效果 */
h1::before,
h2::before,
h3::before,
h4::before,
h5::before,
h6::before {
-webkit-animation: ccc 1.6s linear infinite;
animation: ccc 1.6s linear infinite;
/* 转速:1.6,数字越小转速越快 */
}

@-webkit-keyframes ccc {

0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
/* -1turn 为逆时针转动,1turn 为顺时针转动,相同数字部分记得统一修改: */
to {
-webkit-transform: rotate(-1turn);
transform: rotate(-1turn)
}
}

@keyframes ccc {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}

to {
-webkit-transform: rotate(-1turn);
transform: rotate(-1turn)
}
}

小风车颜色、大小修改

直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#content-inner.layout h1::before {
color: #ef50a8 ;
margin-left: -1.55rem;
font-size: 1.3rem;
margin-top: -0.23rem;
}
#content-inner.layout h2::before {
color: #fb7061 ;
margin-left: -1.35rem;
font-size: 1.1rem;
margin-top: -0.12rem;
}
#content-inner.layout h3::before {
color: #ffbf00 ;
margin-left: -1.22rem;
font-size: 0.95rem;
margin-top: -0.09rem;
}
#content-inner.layout h4::before {
color: #a9e000 ;
margin-left: -1.05rem;
font-size: 0.8rem;
margin-top: -0.09rem;
}
#content-inner.layout h5::before {
color: #57c850 ;
margin-left: -0.9rem;
font-size: 0.7rem;
margin-top: 0.0rem;
}
#content-inner.layout h6::before {
color: #5ec1e0 ;
margin-left: -0.9rem;
font-size: 0.66rem;
margin-top: 0.0rem;
}

小风车hover效果

鼠标碰到小风车转速变慢及变色

设置鼠标碰到标题时,小风车跟随标题变色,且像是被光标阻碍了,转速变慢。鼠标离开恢复转速。也可以设置为 none 鼠标碰到停止转动。

1
2
3
4
5
6
7
8
9
10

#content-inner.layout h1:hover, #content-inner.layout h2:hover, #content-inner.layout h3:hover, #content-inner.layout h4:hover, #content-inner.layout h5:hover, #content-inner.layout h6:hover {
color: #49b1f5 ;
}
#content-inner.layout h1:hover::before, #content-inner.layout h2:hover::before, #content-inner.layout h3:hover::before, #content-inner.layout h4:hover::before, #content-inner.layout h5:hover::before, #content-inner.layout h6:hover::before {
color: #49b1f5 ;
-webkit-animation: ccc 3.2s linear infinite ;
animation: ccc 3.2s linear infinite ;
}

\n
\n
"},{"title":"java面试基础","abbrlink":"af3c95f0","date":"2021-05-21T06:12:33.000Z","description":"关于Java面试会问的一些Java知识点总结,包括类与对象的基本特性、Java虚拟机、字符串、多线程、Java异常、Java IO操作等知识点","_content":"\n\n\n# Java面试基础\n\n\n\n## 面向对象和面向过程的区别\n\n- **面向过程** :**面向过程性能比面向对象高。** 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。**但是**面向过程没有面向对象易维护、易复用、易扩展。\n- **面向对象** :**面向对象易维护、易复用、易扩展。** 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,**面向对象性能比面向过程低**。\n\n## Java语言有哪些特点\n\n1. 面向对象(封装,继承,多态);\n2. 跨平台性( Java 虚拟机实现平台无关性);\n3. 可靠性;\n4. 安全性;\n5. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持)\n6. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);\n7. 编译与解释并存\n\n\n\n## jvm、jdk、jre\n\n- JVM是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。\n\n- JDK 是 Java Development Kit,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。\n- JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。\n\n## Java和C++对比\n\n- 都是面向对象的语言,都支持封装、继承和多态\n- Java 不提供指针来直接访问内存,程序内存更加安全\n- Java 有自动内存管理机制,不需要程序员手动释放无用内存\n- Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。\n\n\n\n## 字符型常量和字符串常量的区别\n\n1. 形式上:字符常量是**单引号**引起的一个字符; 字符串常量是**双引号**引起的若干个字符\n2. 含义上:字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)\n3. 占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节 (**注意: char 在 Java 中占两个字节**)\n\n\n\n## 构造器Constructor是否可被override\n\nConstructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。\n\n\n\n## 重载和重写的区别\n\n重载:同样的一个方法能够根据输入数据的不同,做出不同的处理\n\n重写:当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法\n\n\n\n## Java 面向对象编程三大特性:封装 继承 多态\n\n### 封装\n\n> 1. 属性私有化\n> 2. 提供方法来访问私有属性\n\n封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。\n\n### 继承\n\n继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。\n\n**关于继承如下 3 点请记住:**\n\n1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,**只是拥有**。\n2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。\n3. 子类可以用自己的方式实现父类的方法。\n\n### 多态\n\n所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。\n\n在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。\n\n\n\n## StringBuffer和StringBuilder\n\n两者的区别是什么? String 为什么是不可变的?\n\n### 可变性\n\n简单的来说:`String` 类中使用 `final` 关键字修饰字符数组来保存字符串,`private final char value[]`,所以 String 对象是不可变的。\n\n而 `StringBuilder` 与 `StringBuffer` 都继承自 `AbstractStringBuilder` 类,在 `AbstractStringBuilder` 中也是使用字符数组保存字符串`char[]value` 但是没有用 `final` 关键字修饰,所以这两种对象都是可变的。\n\n`StringBuilder` 与 `StringBuffer` 的构造方法都是调用父类构造方法也就是 `AbstractStringBuilder` 实现的,大家可以自行查阅源码。\n\n### 线程安全性\n\n- `String` 中的对象是不可变的,也就可以理解为常量,线程安全。\n\n- `AbstractStringBuilder` 是 `StringBuilder` 与 `StringBuffer` 的公共父类,定义了一些字符串的基本操作,如 `expandCapacity`、`append`、`insert`、`indexOf` 等公共方法。\n\n- `StringBuffer` 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。\n\n- `StringBuilder` 并没有对方法进行加同步锁,所以是**非**线程安全的。\n\n### 性能\n\n- 每次对 `String` 类型进行改变的时候,都会生成一个新的 `String` 对象,然后将指针指向新的 `String` 对象。\n\n- 而`StringBuffer` 每次都会对 `StringBuffer` 对象本身进行操作,而不是生成新的对象并改变对象引用。\n- 相同情况下使用 `StringBuilder` 相比使用 `StringBuffer` 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。\n\n> 对于三者使用的总结:\n>\n> 1. 操作少量的数据: 适用 `String`\n> 2. 单线程操作字符串缓冲区下操作大量数据: 适用 `StringBuilder`\n> 3. 多线程操作字符串缓冲区下操作大量数据: 适用 `StringBuffer`\n\n## 自动装箱与拆箱\n\n- 装箱:将基本类型用它们对应的引用类型包装起来;\n- 拆箱:将包装类型转换为基本数据类型;\n\n\n\n## 在一个静态方法内调用一个非静态成员为什么是非法的\n\n由于静态方法可以不通过对象进行调用,因此在静态方法里:\n\n- 不能调用其他非静态变量\n- 不可以访问非静态变量成员\n\n## 在Java中定义一个不做事且没有参数的构造方法的作用\n\nJava 程序在实例化子类之前(即执行**子类的构造方法**之前),如果没有用 `super()`来调用父类特定的构造方法,则会默认调用父类中“没有参数的构造方法”。\n\n若父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 `super()`来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。\n\n解决办法:在父类里加上一个不做事且没有参数的构造方法。\n\n详细见:[super关键字](D:\\Users\\74452\\Desktop\\JavaLearning\\javaSE\\14-接口与继承.md)\n\n\n\n## 接口和抽象类的区别\n\n1. 接口的方法默认是 `public`,所有方法在接口中不能有实现(Java 8开始接口方法可以有**默认实现**),而抽象类可以有非抽象的方法。\n2. 接口中除了 `static`、`final` 变量,不能有其他变量,而抽象类中则不一定。\n3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 `extends` 关键字扩展多个接口。\n4. 接口方法默认修饰符是 `public`,抽象方法可以有 `public`、`protected` 和 `default` 这些修饰符(抽象方法就是为了被重写所以不能使用 `private` 关键字修饰!)。\n5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。\n\n> 备注:\n>\n> 1. 在 JDK8 中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。\n> 2. jdk9 的接口被允许定义私有方法 。\n\n\n\n## 对象实体与对象引用\n\n- 用new 运算符创建对象实例(对象实例在堆内存中)\n\n- 对象引用指向对象实例(对象引用存放在栈内存中)\n\n一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);\n\n一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。\n\n\n\n## 类的构造方法\n\n类的构造方法主要作用是完成对类对象的初始化工作。\n\n一个类没有声明构造方法也可以执行,因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。\n\n\n\n## 静态方法与实例方法区别\n\n1. 在外部调用静态方法时,可以使用\"`类名.方法名`\"的方式,也可以使用\"`对象名.方法名`\"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。\n2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。\n\n\n\n## 对象相等vs引用相等\n\n对象的相等,指的是内存中存放的内容是否相等。\n\n而引用相等,指的是他们指向的内存地址是否相等。\n\n\n\n## ==号与equals方法\n\n基本数据类型`==`比较的是值,而引用数据类型`==`比较的是内存地址\n\n注意:\n\n1. 类没有覆盖 equals() 方法时, equals() 比较的是两个对象的内存地址,相当于用 `==`号\n2. String 中的 equals 方法是被重写过的,因此String 的 equals 方法比较的是对象的值\n\n3. 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。\n\n\n\n## hashCode 与 equals \n\n为什么重写 `equals` 时必须重写 `hashCode` 方法:\n\n1. hashcode用来记录对象存放的地址(理解可能不到位),如果两个对象相等,则 hashcode 一定也是相同的。\n\n2. 两个对象相等,对两个对象分别调用 equals 方法都返回 true。\n\n3. 但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。\n4. 因此,equals 方法被覆盖过,则 `hashCode` 方法也必须被覆盖\n\n\n\n## Java值传递\n\nJava 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个**拷贝**,即方法不能修改传递给它的任何参数变量的内容。\n\n> 与c/c++不同,c/c++提供了值传递和引用传递\n\n\n\n## 线程、进程\n\n### 简述\n\n**线程(thread)** 是操作系统能够进行运算调度的最小单位\n\n同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。\n\n---\n\n**进程**是系统进行资源( CPU 时间,内存空间,文件,输入输出设备的使用权等等)分配和调度的基本单位\n\n\n\n### 线程生命周期的几种基本状态\n\n1. 线程创建之后它将处于 **NEW(新建)** 状态\n\n2. 调用 `start()` 方法后线程这时候处于 **READY(就绪)** 状态。\n3. 可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 **RUNNING(运行)** 状态。\n4. 线程执行 `wait()`方法之后,线程进入 **WAITING(等待)**状态\n5. 当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。\n6. 线程在执行 Runnable 的`run()`方法之后将会进入到 **TERMINATED(终止)** 状态。\n\n![Java线程状态变迁](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/19-1-29/Java%20%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E5%8F%98%E8%BF%81.png)\n\n\n\n## final关键字\n\nfinal 关键字主要用在三个地方:变量、方法、类。\n\n1. 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。\n2. 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法。\n3. 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。\n\n\n\n\n\n## Java异常处理\n\n结构图:\n\n\"img\"\n\n\n\n\"img\"\n\n\n\n\n\n`Exception` 和 `Error` 二者都是 Java 异常处理的重要子类,各自都包含大量子类。\n\n- `Exception`:程序本身可以处理的异常,可以通过 `catch` 来进行捕获。`Exception` 又可以分为检查异常(必须处理) `Check Exception` 和 不检查异常(可以不处理) `Uncheck Exception`。\n- `Error` :`Error` 属于程序无法处理的错误 ,我们没办法通过 `catch` 来进行捕获 。例如,Java 虚拟机运行错误(`Virtual MachineError`)、虚拟机内存不够错误(`OutOfMemoryError`)、类定义错误(`NoClassDefFoundError`)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。\n\n\n\n1. 检查异常\n\n 除了``RuntimeException`及其子类以外,其他的`Exception`类及其子类都属于检查异常。常见的受检查异常有:\n\n - IO 相关的异常\n - `ClassNotFoundException` \n - `SQLException`……\n\n \n\n2. 不检查异常\n\n `RuntimeException` 及其子类都统称为非受检查异常,例如:\n\n - `NullPointerException`\n - `NumberFormatException`(字符串转换为数字)\n - `ArrayIndexOutOfBoundsException`(数组越界)\n - `ClassCastException`(类型转换错误)\n - `ArithmeticException`(算术错误)等\n\n\n\n**在以下 3 种特殊情况下,`finally` 块不会被执行:**\n\n1. 在 try 或 finally 块中用了 `System.exit(int)`退出程序。但是,如果 `System.exit(int)` 在异常语句之后,`finally` 还是会被执行\n2. 程序所在的线程死亡。\n3. 关闭 CPU。\n\n> **注意:** 当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖try语句的返回值。\n\n\n\n## 获取用键盘输入常用的两种方法\n\n1. 通过 Scanner\n\n```java\nScanner input = new Scanner(System.in);\nString s = input.nextLine();\ninput.close();Copy to clipboardErrorCopied\n```\n\n\n\n2. 通过 BufferedReader\n\n```java\nBufferedReader input = new BufferedReader(new InputStreamReader(System.in));\nString s = input.readLine();\n```\n\n\n\n## Java 中 IO 流分类\n\n- 按照流的流向分,可以分为输入流和输出流;\n- 按照操作单元划分,可以划分为字节流和字符流;\n- 按照流的角色划分为节点流和处理流\n\n\n\n> Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来:\n>\n> - InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。\n> - OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。\n\n\n\n> 为什么要有字符流?\n>\n> 字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。\n>\n> - 音频文件、图片等二进制文件用字节流比较好,\n>\n> - 涉及到字符文本文件使用字符流比较好\n\n","source":"_posts/Java面试/java面试题.md","raw":"---\ntitle: java面试基础\ntags:\n - Java面试\ncategories:\n - java\n - java面试\nabbrlink: af3c95f0\ndate: 2021-05-21 14:12:33\ndescription: 关于Java面试会问的一些Java知识点总结,包括类与对象的基本特性、Java虚拟机、字符串、多线程、Java异常、Java IO操作等知识点\n---\n\n\n\n# Java面试基础\n\n\n\n## 面向对象和面向过程的区别\n\n- **面向过程** :**面向过程性能比面向对象高。** 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。**但是**面向过程没有面向对象易维护、易复用、易扩展。\n- **面向对象** :**面向对象易维护、易复用、易扩展。** 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,**面向对象性能比面向过程低**。\n\n## Java语言有哪些特点\n\n1. 面向对象(封装,继承,多态);\n2. 跨平台性( Java 虚拟机实现平台无关性);\n3. 可靠性;\n4. 安全性;\n5. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持)\n6. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);\n7. 编译与解释并存\n\n\n\n## jvm、jdk、jre\n\n- JVM是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。\n\n- JDK 是 Java Development Kit,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。\n- JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。\n\n## Java和C++对比\n\n- 都是面向对象的语言,都支持封装、继承和多态\n- Java 不提供指针来直接访问内存,程序内存更加安全\n- Java 有自动内存管理机制,不需要程序员手动释放无用内存\n- Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。\n\n\n\n## 字符型常量和字符串常量的区别\n\n1. 形式上:字符常量是**单引号**引起的一个字符; 字符串常量是**双引号**引起的若干个字符\n2. 含义上:字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)\n3. 占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节 (**注意: char 在 Java 中占两个字节**)\n\n\n\n## 构造器Constructor是否可被override\n\nConstructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。\n\n\n\n## 重载和重写的区别\n\n重载:同样的一个方法能够根据输入数据的不同,做出不同的处理\n\n重写:当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法\n\n\n\n## Java 面向对象编程三大特性:封装 继承 多态\n\n### 封装\n\n> 1. 属性私有化\n> 2. 提供方法来访问私有属性\n\n封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。\n\n### 继承\n\n继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。\n\n**关于继承如下 3 点请记住:**\n\n1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,**只是拥有**。\n2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。\n3. 子类可以用自己的方式实现父类的方法。\n\n### 多态\n\n所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。\n\n在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。\n\n\n\n## StringBuffer和StringBuilder\n\n两者的区别是什么? String 为什么是不可变的?\n\n### 可变性\n\n简单的来说:`String` 类中使用 `final` 关键字修饰字符数组来保存字符串,`private final char value[]`,所以 String 对象是不可变的。\n\n而 `StringBuilder` 与 `StringBuffer` 都继承自 `AbstractStringBuilder` 类,在 `AbstractStringBuilder` 中也是使用字符数组保存字符串`char[]value` 但是没有用 `final` 关键字修饰,所以这两种对象都是可变的。\n\n`StringBuilder` 与 `StringBuffer` 的构造方法都是调用父类构造方法也就是 `AbstractStringBuilder` 实现的,大家可以自行查阅源码。\n\n### 线程安全性\n\n- `String` 中的对象是不可变的,也就可以理解为常量,线程安全。\n\n- `AbstractStringBuilder` 是 `StringBuilder` 与 `StringBuffer` 的公共父类,定义了一些字符串的基本操作,如 `expandCapacity`、`append`、`insert`、`indexOf` 等公共方法。\n\n- `StringBuffer` 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。\n\n- `StringBuilder` 并没有对方法进行加同步锁,所以是**非**线程安全的。\n\n### 性能\n\n- 每次对 `String` 类型进行改变的时候,都会生成一个新的 `String` 对象,然后将指针指向新的 `String` 对象。\n\n- 而`StringBuffer` 每次都会对 `StringBuffer` 对象本身进行操作,而不是生成新的对象并改变对象引用。\n- 相同情况下使用 `StringBuilder` 相比使用 `StringBuffer` 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。\n\n> 对于三者使用的总结:\n>\n> 1. 操作少量的数据: 适用 `String`\n> 2. 单线程操作字符串缓冲区下操作大量数据: 适用 `StringBuilder`\n> 3. 多线程操作字符串缓冲区下操作大量数据: 适用 `StringBuffer`\n\n## 自动装箱与拆箱\n\n- 装箱:将基本类型用它们对应的引用类型包装起来;\n- 拆箱:将包装类型转换为基本数据类型;\n\n\n\n## 在一个静态方法内调用一个非静态成员为什么是非法的\n\n由于静态方法可以不通过对象进行调用,因此在静态方法里:\n\n- 不能调用其他非静态变量\n- 不可以访问非静态变量成员\n\n## 在Java中定义一个不做事且没有参数的构造方法的作用\n\nJava 程序在实例化子类之前(即执行**子类的构造方法**之前),如果没有用 `super()`来调用父类特定的构造方法,则会默认调用父类中“没有参数的构造方法”。\n\n若父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 `super()`来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。\n\n解决办法:在父类里加上一个不做事且没有参数的构造方法。\n\n详细见:[super关键字](D:\\Users\\74452\\Desktop\\JavaLearning\\javaSE\\14-接口与继承.md)\n\n\n\n## 接口和抽象类的区别\n\n1. 接口的方法默认是 `public`,所有方法在接口中不能有实现(Java 8开始接口方法可以有**默认实现**),而抽象类可以有非抽象的方法。\n2. 接口中除了 `static`、`final` 变量,不能有其他变量,而抽象类中则不一定。\n3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 `extends` 关键字扩展多个接口。\n4. 接口方法默认修饰符是 `public`,抽象方法可以有 `public`、`protected` 和 `default` 这些修饰符(抽象方法就是为了被重写所以不能使用 `private` 关键字修饰!)。\n5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。\n\n> 备注:\n>\n> 1. 在 JDK8 中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。\n> 2. jdk9 的接口被允许定义私有方法 。\n\n\n\n## 对象实体与对象引用\n\n- 用new 运算符创建对象实例(对象实例在堆内存中)\n\n- 对象引用指向对象实例(对象引用存放在栈内存中)\n\n一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);\n\n一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。\n\n\n\n## 类的构造方法\n\n类的构造方法主要作用是完成对类对象的初始化工作。\n\n一个类没有声明构造方法也可以执行,因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。\n\n\n\n## 静态方法与实例方法区别\n\n1. 在外部调用静态方法时,可以使用\"`类名.方法名`\"的方式,也可以使用\"`对象名.方法名`\"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。\n2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。\n\n\n\n## 对象相等vs引用相等\n\n对象的相等,指的是内存中存放的内容是否相等。\n\n而引用相等,指的是他们指向的内存地址是否相等。\n\n\n\n## ==号与equals方法\n\n基本数据类型`==`比较的是值,而引用数据类型`==`比较的是内存地址\n\n注意:\n\n1. 类没有覆盖 equals() 方法时, equals() 比较的是两个对象的内存地址,相当于用 `==`号\n2. String 中的 equals 方法是被重写过的,因此String 的 equals 方法比较的是对象的值\n\n3. 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。\n\n\n\n## hashCode 与 equals \n\n为什么重写 `equals` 时必须重写 `hashCode` 方法:\n\n1. hashcode用来记录对象存放的地址(理解可能不到位),如果两个对象相等,则 hashcode 一定也是相同的。\n\n2. 两个对象相等,对两个对象分别调用 equals 方法都返回 true。\n\n3. 但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。\n4. 因此,equals 方法被覆盖过,则 `hashCode` 方法也必须被覆盖\n\n\n\n## Java值传递\n\nJava 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个**拷贝**,即方法不能修改传递给它的任何参数变量的内容。\n\n> 与c/c++不同,c/c++提供了值传递和引用传递\n\n\n\n## 线程、进程\n\n### 简述\n\n**线程(thread)** 是操作系统能够进行运算调度的最小单位\n\n同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。\n\n---\n\n**进程**是系统进行资源( CPU 时间,内存空间,文件,输入输出设备的使用权等等)分配和调度的基本单位\n\n\n\n### 线程生命周期的几种基本状态\n\n1. 线程创建之后它将处于 **NEW(新建)** 状态\n\n2. 调用 `start()` 方法后线程这时候处于 **READY(就绪)** 状态。\n3. 可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 **RUNNING(运行)** 状态。\n4. 线程执行 `wait()`方法之后,线程进入 **WAITING(等待)**状态\n5. 当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。\n6. 线程在执行 Runnable 的`run()`方法之后将会进入到 **TERMINATED(终止)** 状态。\n\n![Java线程状态变迁](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/19-1-29/Java%20%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E5%8F%98%E8%BF%81.png)\n\n\n\n## final关键字\n\nfinal 关键字主要用在三个地方:变量、方法、类。\n\n1. 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。\n2. 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法。\n3. 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。\n\n\n\n\n\n## Java异常处理\n\n结构图:\n\n\"img\"\n\n\n\n\"img\"\n\n\n\n\n\n`Exception` 和 `Error` 二者都是 Java 异常处理的重要子类,各自都包含大量子类。\n\n- `Exception`:程序本身可以处理的异常,可以通过 `catch` 来进行捕获。`Exception` 又可以分为检查异常(必须处理) `Check Exception` 和 不检查异常(可以不处理) `Uncheck Exception`。\n- `Error` :`Error` 属于程序无法处理的错误 ,我们没办法通过 `catch` 来进行捕获 。例如,Java 虚拟机运行错误(`Virtual MachineError`)、虚拟机内存不够错误(`OutOfMemoryError`)、类定义错误(`NoClassDefFoundError`)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。\n\n\n\n1. 检查异常\n\n 除了``RuntimeException`及其子类以外,其他的`Exception`类及其子类都属于检查异常。常见的受检查异常有:\n\n - IO 相关的异常\n - `ClassNotFoundException` \n - `SQLException`……\n\n \n\n2. 不检查异常\n\n `RuntimeException` 及其子类都统称为非受检查异常,例如:\n\n - `NullPointerException`\n - `NumberFormatException`(字符串转换为数字)\n - `ArrayIndexOutOfBoundsException`(数组越界)\n - `ClassCastException`(类型转换错误)\n - `ArithmeticException`(算术错误)等\n\n\n\n**在以下 3 种特殊情况下,`finally` 块不会被执行:**\n\n1. 在 try 或 finally 块中用了 `System.exit(int)`退出程序。但是,如果 `System.exit(int)` 在异常语句之后,`finally` 还是会被执行\n2. 程序所在的线程死亡。\n3. 关闭 CPU。\n\n> **注意:** 当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖try语句的返回值。\n\n\n\n## 获取用键盘输入常用的两种方法\n\n1. 通过 Scanner\n\n```java\nScanner input = new Scanner(System.in);\nString s = input.nextLine();\ninput.close();Copy to clipboardErrorCopied\n```\n\n\n\n2. 通过 BufferedReader\n\n```java\nBufferedReader input = new BufferedReader(new InputStreamReader(System.in));\nString s = input.readLine();\n```\n\n\n\n## Java 中 IO 流分类\n\n- 按照流的流向分,可以分为输入流和输出流;\n- 按照操作单元划分,可以划分为字节流和字符流;\n- 按照流的角色划分为节点流和处理流\n\n\n\n> Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来:\n>\n> - InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。\n> - OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。\n\n\n\n> 为什么要有字符流?\n>\n> 字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。\n>\n> - 音频文件、图片等二进制文件用字节流比较好,\n>\n> - 涉及到字符文本文件使用字符流比较好\n\n","slug":"Java面试/java面试题","published":1,"updated":"2021-09-04T02:01:08.705Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6tu00h3akve8scabvi5","content":"

Java面试基础

面向对象和面向过程的区别

    \n
  • 面向过程面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。但是面向过程没有面向对象易维护、易复用、易扩展。
  • \n
  • 面向对象面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低
  • \n
\n

Java语言有哪些特点

    \n
  1. 面向对象(封装,继承,多态);
  2. \n
  3. 跨平台性( Java 虚拟机实现平台无关性);
  4. \n
  5. 可靠性;
  6. \n
  7. 安全性;
  8. \n
  9. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持)
  10. \n
  11. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);
  12. \n
  13. 编译与解释并存
  14. \n
\n

jvm、jdk、jre

    \n
  • JVM是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。

    \n
  • \n
  • JDK 是 Java Development Kit,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。

    \n
  • \n
  • JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。
  • \n
\n

Java和C++对比

    \n
  • 都是面向对象的语言,都支持封装、继承和多态
  • \n
  • Java 不提供指针来直接访问内存,程序内存更加安全
  • \n
  • Java 有自动内存管理机制,不需要程序员手动释放无用内存
  • \n
  • Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
  • \n
\n

字符型常量和字符串常量的区别

    \n
  1. 形式上:字符常量是单引号引起的一个字符; 字符串常量是双引号引起的若干个字符
  2. \n
  3. 含义上:字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)
  4. \n
  5. 占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节 (注意: char 在 Java 中占两个字节)
  6. \n
\n

构造器Constructor是否可被override

Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。

\n

重载和重写的区别

重载:同样的一个方法能够根据输入数据的不同,做出不同的处理

\n

重写:当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法

\n

Java 面向对象编程三大特性:封装 继承 多态

封装

\n
    \n
  1. 属性私有化
  2. \n
  3. 提供方法来访问私有属性
  4. \n
\n
\n

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

\n

继承

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

\n

关于继承如下 3 点请记住:

\n
    \n
  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有
  2. \n
  3. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
  4. \n
  5. 子类可以用自己的方式实现父类的方法。
  6. \n
\n

多态

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

\n

在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

\n

StringBuffer和StringBuilder

两者的区别是什么? String 为什么是不可变的?

\n

可变性

简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。

\n

StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。

\n

StringBuilderStringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,大家可以自行查阅源码。

\n

线程安全性

    \n
  • String 中的对象是不可变的,也就可以理解为常量,线程安全。

    \n
  • \n
  • AbstractStringBuilderStringBuilderStringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacityappendinsertindexOf 等公共方法。

    \n
  • \n
  • StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。

    \n
  • \n
  • StringBuilder 并没有对方法进行加同步锁,所以是线程安全的。

    \n
  • \n
\n

性能

    \n
  • 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。

    \n
  • \n
  • StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。

    \n
  • \n
  • 相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
  • \n
\n
\n

对于三者使用的总结:

\n
    \n
  1. 操作少量的数据: 适用 String
  2. \n
  3. 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  4. \n
  5. 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer
  6. \n
\n
\n

自动装箱与拆箱

    \n
  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • \n
  • 拆箱:将包装类型转换为基本数据类型;
  • \n
\n

在一个静态方法内调用一个非静态成员为什么是非法的

由于静态方法可以不通过对象进行调用,因此在静态方法里:

\n
    \n
  • 不能调用其他非静态变量
  • \n
  • 不可以访问非静态变量成员
  • \n
\n

在Java中定义一个不做事且没有参数的构造方法的作用

Java 程序在实例化子类之前(即执行子类的构造方法之前),如果没有用 super()来调用父类特定的构造方法,则会默认调用父类中“没有参数的构造方法”。

\n

若父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。

\n

解决办法:在父类里加上一个不做事且没有参数的构造方法。

\n

详细见:super关键字

\n

接口和抽象类的区别

    \n
  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  2. \n
  3. 接口中除了 staticfinal 变量,不能有其他变量,而抽象类中则不一定。
  4. \n
  5. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
  6. \n
  7. 接口方法默认修饰符是 public,抽象方法可以有 publicprotecteddefault 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!)。
  8. \n
  9. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
  10. \n
\n
\n

备注:

\n
    \n
  1. 在 JDK8 中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。
  2. \n
  3. jdk9 的接口被允许定义私有方法 。
  4. \n
\n
\n

对象实体与对象引用

    \n
  • 用new 运算符创建对象实例(对象实例在堆内存中)

    \n
  • \n
  • 对象引用指向对象实例(对象引用存放在栈内存中)

    \n
  • \n
\n

一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);

\n

一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。

\n

类的构造方法

类的构造方法主要作用是完成对类对象的初始化工作。

\n

一个类没有声明构造方法也可以执行,因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。

\n

静态方法与实例方法区别

    \n
  1. 在外部调用静态方法时,可以使用”类名.方法名“的方式,也可以使用”对象名.方法名“的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
  2. \n
  3. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。
  4. \n
\n

对象相等vs引用相等

对象的相等,指的是内存中存放的内容是否相等。

\n

而引用相等,指的是他们指向的内存地址是否相等。

\n

==号与equals方法

基本数据类型==比较的是值,而引用数据类型==比较的是内存地址

\n

注意:

\n
    \n
  1. 类没有覆盖 equals() 方法时, equals() 比较的是两个对象的内存地址,相当于用 ==
  2. \n
  3. String 中的 equals 方法是被重写过的,因此String 的 equals 方法比较的是对象的值

    \n
  4. \n
  5. 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。

    \n
  6. \n
\n

hashCode 与 equals

为什么重写 equals 时必须重写 hashCode 方法:

\n
    \n
  1. hashcode用来记录对象存放的地址(理解可能不到位),如果两个对象相等,则 hashcode 一定也是相同的。

    \n
  2. \n
  3. 两个对象相等,对两个对象分别调用 equals 方法都返回 true。

    \n
  4. \n
  5. 但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。

    \n
  6. \n
  7. 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
  8. \n
\n

Java值传递

Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,即方法不能修改传递给它的任何参数变量的内容。

\n
\n

与c/c++不同,c/c++提供了值传递和引用传递

\n
\n

线程、进程

简述

线程(thread) 是操作系统能够进行运算调度的最小单位

\n

同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

\n
\n

进程是系统进行资源( CPU 时间,内存空间,文件,输入输出设备的使用权等等)分配和调度的基本单位

\n

线程生命周期的几种基本状态

    \n
  1. 线程创建之后它将处于 NEW(新建) 状态

    \n
  2. \n
  3. 调用 start() 方法后线程这时候处于 READY(就绪) 状态。

    \n
  4. \n
  5. 可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 RUNNING(运行) 状态。
  6. \n
  7. 线程执行 wait()方法之后,线程进入 WAITING(等待)状态
  8. \n
  9. 当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。
  10. \n
  11. 线程在执行 Runnable 的run()方法之后将会进入到 TERMINATED(终止) 状态。
  12. \n
\n

\"Java线程状态变迁\"

\n

final关键字

final 关键字主要用在三个地方:变量、方法、类。

\n
    \n
  1. 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
  2. \n
  3. 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法。
  4. \n
  5. 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。
  6. \n
\n

Java异常处理

结构图:

\n

\"img\"

\n

\"img\"

\n

ExceptionError 二者都是 Java 异常处理的重要子类,各自都包含大量子类。

\n
    \n
  • Exception:程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为检查异常(必须处理) Check Exception 和 不检查异常(可以不处理) Uncheck Exception
  • \n
  • ErrorError 属于程序无法处理的错误 ,我们没办法通过 catch 来进行捕获 。例如,Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
  • \n
\n
    \n
  1. 检查异常

    \n

    除了`RuntimeException及其子类以外,其他的Exception类及其子类都属于检查异常。常见的受检查异常有:

    \n
      \n
    • IO 相关的异常
    • \n
    • ClassNotFoundException
    • \n
    • SQLException……
    • \n
    \n
  2. \n
\n
    \n
  1. 不检查异常

    \n

    RuntimeException 及其子类都统称为非受检查异常,例如:

    \n
      \n
    • NullPointerException
    • \n
    • NumberFormatException(字符串转换为数字)
    • \n
    • ArrayIndexOutOfBoundsException(数组越界)
    • \n
    • ClassCastException(类型转换错误)
    • \n
    • ArithmeticException(算术错误)等
    • \n
    \n
  2. \n
\n

在以下 3 种特殊情况下,finally 块不会被执行:

\n
    \n
  1. 在 try 或 finally 块中用了 System.exit(int)退出程序。但是,如果 System.exit(int) 在异常语句之后,finally 还是会被执行
  2. \n
  3. 程序所在的线程死亡。
  4. \n
  5. 关闭 CPU。
  6. \n
\n
\n

注意: 当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖try语句的返回值。

\n
\n

获取用键盘输入常用的两种方法

    \n
  1. 通过 Scanner
  2. \n
\n
1
2
3
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();Copy to clipboardErrorCopied
\n
    \n
  1. 通过 BufferedReader
  2. \n
\n
1
2
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
\n

Java 中 IO 流分类

    \n
  • 按照流的流向分,可以分为输入流和输出流;
  • \n
  • 按照操作单元划分,可以划分为字节流和字符流;
  • \n
  • 按照流的角色划分为节点流和处理流
  • \n
\n
\n

Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来:

\n
    \n
  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • \n
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
  • \n
\n

为什么要有字符流?

\n

字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。

\n
    \n
  • 音频文件、图片等二进制文件用字节流比较好,

    \n
  • \n
  • 涉及到字符文本文件使用字符流比较好

    \n
  • \n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

Java面试基础

面向对象和面向过程的区别

    \n
  • 面向过程面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。但是面向过程没有面向对象易维护、易复用、易扩展。
  • \n
  • 面向对象面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低
  • \n
\n

Java语言有哪些特点

    \n
  1. 面向对象(封装,继承,多态);
  2. \n
  3. 跨平台性( Java 虚拟机实现平台无关性);
  4. \n
  5. 可靠性;
  6. \n
  7. 安全性;
  8. \n
  9. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持)
  10. \n
  11. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);
  12. \n
  13. 编译与解释并存
  14. \n
\n

jvm、jdk、jre

    \n
  • JVM是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。

    \n
  • \n
  • JDK 是 Java Development Kit,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。

    \n
  • \n
  • JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。
  • \n
\n

Java和C++对比

    \n
  • 都是面向对象的语言,都支持封装、继承和多态
  • \n
  • Java 不提供指针来直接访问内存,程序内存更加安全
  • \n
  • Java 有自动内存管理机制,不需要程序员手动释放无用内存
  • \n
  • Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
  • \n
\n

字符型常量和字符串常量的区别

    \n
  1. 形式上:字符常量是单引号引起的一个字符; 字符串常量是双引号引起的若干个字符
  2. \n
  3. 含义上:字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)
  4. \n
  5. 占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节 (注意: char 在 Java 中占两个字节)
  6. \n
\n

构造器Constructor是否可被override

Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。

\n

重载和重写的区别

重载:同样的一个方法能够根据输入数据的不同,做出不同的处理

\n

重写:当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法

\n

Java 面向对象编程三大特性:封装 继承 多态

封装

\n
    \n
  1. 属性私有化
  2. \n
  3. 提供方法来访问私有属性
  4. \n
\n
\n

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

\n

继承

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

\n

关于继承如下 3 点请记住:

\n
    \n
  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有
  2. \n
  3. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
  4. \n
  5. 子类可以用自己的方式实现父类的方法。
  6. \n
\n

多态

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

\n

在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

\n

StringBuffer和StringBuilder

两者的区别是什么? String 为什么是不可变的?

\n

可变性

简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。

\n

StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。

\n

StringBuilderStringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,大家可以自行查阅源码。

\n

线程安全性

    \n
  • String 中的对象是不可变的,也就可以理解为常量,线程安全。

    \n
  • \n
  • AbstractStringBuilderStringBuilderStringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacityappendinsertindexOf 等公共方法。

    \n
  • \n
  • StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。

    \n
  • \n
  • StringBuilder 并没有对方法进行加同步锁,所以是线程安全的。

    \n
  • \n
\n

性能

    \n
  • 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。

    \n
  • \n
  • StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。

    \n
  • \n
  • 相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
  • \n
\n
\n

对于三者使用的总结:

\n
    \n
  1. 操作少量的数据: 适用 String
  2. \n
  3. 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  4. \n
  5. 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer
  6. \n
\n
\n

自动装箱与拆箱

    \n
  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • \n
  • 拆箱:将包装类型转换为基本数据类型;
  • \n
\n

在一个静态方法内调用一个非静态成员为什么是非法的

由于静态方法可以不通过对象进行调用,因此在静态方法里:

\n
    \n
  • 不能调用其他非静态变量
  • \n
  • 不可以访问非静态变量成员
  • \n
\n

在Java中定义一个不做事且没有参数的构造方法的作用

Java 程序在实例化子类之前(即执行子类的构造方法之前),如果没有用 super()来调用父类特定的构造方法,则会默认调用父类中“没有参数的构造方法”。

\n

若父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。

\n

解决办法:在父类里加上一个不做事且没有参数的构造方法。

\n

详细见:super关键字

\n

接口和抽象类的区别

    \n
  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  2. \n
  3. 接口中除了 staticfinal 变量,不能有其他变量,而抽象类中则不一定。
  4. \n
  5. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
  6. \n
  7. 接口方法默认修饰符是 public,抽象方法可以有 publicprotecteddefault 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!)。
  8. \n
  9. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
  10. \n
\n
\n

备注:

\n
    \n
  1. 在 JDK8 中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。
  2. \n
  3. jdk9 的接口被允许定义私有方法 。
  4. \n
\n
\n

对象实体与对象引用

    \n
  • 用new 运算符创建对象实例(对象实例在堆内存中)

    \n
  • \n
  • 对象引用指向对象实例(对象引用存放在栈内存中)

    \n
  • \n
\n

一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);

\n

一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。

\n

类的构造方法

类的构造方法主要作用是完成对类对象的初始化工作。

\n

一个类没有声明构造方法也可以执行,因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。

\n

静态方法与实例方法区别

    \n
  1. 在外部调用静态方法时,可以使用”类名.方法名“的方式,也可以使用”对象名.方法名“的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
  2. \n
  3. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。
  4. \n
\n

对象相等vs引用相等

对象的相等,指的是内存中存放的内容是否相等。

\n

而引用相等,指的是他们指向的内存地址是否相等。

\n

==号与equals方法

基本数据类型==比较的是值,而引用数据类型==比较的是内存地址

\n

注意:

\n
    \n
  1. 类没有覆盖 equals() 方法时, equals() 比较的是两个对象的内存地址,相当于用 ==
  2. \n
  3. String 中的 equals 方法是被重写过的,因此String 的 equals 方法比较的是对象的值

    \n
  4. \n
  5. 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。

    \n
  6. \n
\n

hashCode 与 equals

为什么重写 equals 时必须重写 hashCode 方法:

\n
    \n
  1. hashcode用来记录对象存放的地址(理解可能不到位),如果两个对象相等,则 hashcode 一定也是相同的。

    \n
  2. \n
  3. 两个对象相等,对两个对象分别调用 equals 方法都返回 true。

    \n
  4. \n
  5. 但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。

    \n
  6. \n
  7. 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
  8. \n
\n

Java值传递

Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,即方法不能修改传递给它的任何参数变量的内容。

\n
\n

与c/c++不同,c/c++提供了值传递和引用传递

\n
\n

线程、进程

简述

线程(thread) 是操作系统能够进行运算调度的最小单位

\n

同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

\n
\n

进程是系统进行资源( CPU 时间,内存空间,文件,输入输出设备的使用权等等)分配和调度的基本单位

\n

线程生命周期的几种基本状态

    \n
  1. 线程创建之后它将处于 NEW(新建) 状态

    \n
  2. \n
  3. 调用 start() 方法后线程这时候处于 READY(就绪) 状态。

    \n
  4. \n
  5. 可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 RUNNING(运行) 状态。
  6. \n
  7. 线程执行 wait()方法之后,线程进入 WAITING(等待)状态
  8. \n
  9. 当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。
  10. \n
  11. 线程在执行 Runnable 的run()方法之后将会进入到 TERMINATED(终止) 状态。
  12. \n
\n

\"Java线程状态变迁\"

\n

final关键字

final 关键字主要用在三个地方:变量、方法、类。

\n
    \n
  1. 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
  2. \n
  3. 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法。
  4. \n
  5. 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。
  6. \n
\n

Java异常处理

结构图:

\n

\"img\"

\n

\"img\"

\n

ExceptionError 二者都是 Java 异常处理的重要子类,各自都包含大量子类。

\n
    \n
  • Exception:程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为检查异常(必须处理) Check Exception 和 不检查异常(可以不处理) Uncheck Exception
  • \n
  • ErrorError 属于程序无法处理的错误 ,我们没办法通过 catch 来进行捕获 。例如,Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
  • \n
\n
    \n
  1. 检查异常

    \n

    除了`RuntimeException及其子类以外,其他的Exception类及其子类都属于检查异常。常见的受检查异常有:

    \n
      \n
    • IO 相关的异常
    • \n
    • ClassNotFoundException
    • \n
    • SQLException……
    • \n
    \n
  2. \n
\n
    \n
  1. 不检查异常

    \n

    RuntimeException 及其子类都统称为非受检查异常,例如:

    \n
      \n
    • NullPointerException
    • \n
    • NumberFormatException(字符串转换为数字)
    • \n
    • ArrayIndexOutOfBoundsException(数组越界)
    • \n
    • ClassCastException(类型转换错误)
    • \n
    • ArithmeticException(算术错误)等
    • \n
    \n
  2. \n
\n

在以下 3 种特殊情况下,finally 块不会被执行:

\n
    \n
  1. 在 try 或 finally 块中用了 System.exit(int)退出程序。但是,如果 System.exit(int) 在异常语句之后,finally 还是会被执行
  2. \n
  3. 程序所在的线程死亡。
  4. \n
  5. 关闭 CPU。
  6. \n
\n
\n

注意: 当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖try语句的返回值。

\n
\n

获取用键盘输入常用的两种方法

    \n
  1. 通过 Scanner
  2. \n
\n
1
2
3
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();Copy to clipboardErrorCopied
\n
    \n
  1. 通过 BufferedReader
  2. \n
\n
1
2
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
\n

Java 中 IO 流分类

    \n
  • 按照流的流向分,可以分为输入流和输出流;
  • \n
  • 按照操作单元划分,可以划分为字节流和字符流;
  • \n
  • 按照流的角色划分为节点流和处理流
  • \n
\n
\n

Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来:

\n
    \n
  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • \n
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
  • \n
\n

为什么要有字符流?

\n

字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。

\n
    \n
  • 音频文件、图片等二进制文件用字节流比较好,

    \n
  • \n
  • 涉及到字符文本文件使用字符流比较好

    \n
  • \n
\n
\n"},{"title":"mybatis关联查询(四)","description":"多个表之间如何实现关联查询(一对多、多对一、多对多)","abbrlink":"ba43a8dc","date":"2021-08-26T01:12:46.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n## 关联查询\n\n关联查询分为一对多、多对一、多对多的情形,这里\"一\"指的是个别、个例,\"多\"指的是集体\n\n### 一对多\n\n比如一个班级有多个学生,一个学生只对应一个班级,现在有2张表,一张是班级表,一张是学生表,要查询一个学生对应的班级,即一对多(学生是单个,但班级是一个集合)\n\n下面以用户和地区(一个用户对应一个地区,一个地区有多名用户)两张表来进行联表查询:查找某个用户所在的地区\n\n\n\n[项目地址mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master2)\n\n\n{% note info flat %}\n看这篇文章前要先了解关系型数据库的 主键外键\n{% endnote %}\n\n先创建2个表\n\n```sql\nuse mybatis;\n\n# 创建2个表\ncreate table users(\n id int primary key auto_increment,\n name varchar(11) not null\n);\n\ncreate table area(\n id int primary key auto_increment,\n name varchar(11)\n);\n\n\n-- 给users表添加外键\nalter table users add aid int;\nalter table users add constraint area_id foreign key (aid) references area (id);\n\n\n# 插入数据\ninsert into area(id, name) VALUES (2, \"华南\");\ninsert into area(id, name) VALUES (3, \"东北\");\ninsert into area(id, name) VALUES (9, \"华北\");\n\ninsert into users(id, name, aid) VALUES (1, \"小明\", 2);\ninsert into users(id, name, aid) VALUES (2, \"小王\", 9);\ninsert into users(id, name, aid) VALUES (3, \"小虹\", 2);\ninsert into users(id, name, aid) VALUES (4, \"小天\", 3);\n\n\n\nselect u.id, u.name, a.name from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n\n```\n\n\n\n| users | area |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210827202630533](https://gitee.com/ajream/images/raw/master/img/20210827202634_image-20210827202630533.png) | ![image-20210827211945478](https://gitee.com/ajream/images/raw/master/img/20210827211948_image-20210827211945478.png) |\n\n\n\n\n\n#### 环境配置\n\n\n\n导入包 + resources配置\n\n```xml\n\n \n mysql\n mysql-connector-java\n 8.0.26\n \n \n org.mybatis\n mybatis\n 3.5.6\n \n \n org.projectlombok\n lombok\n 1.18.20\n \n \n junit\n junit\n 4.13\n test\n \n\n\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\nmybatis-config.xml\n\n\n\n```xml\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n\n```\n\n\n\n\n\n#### 进行开发\n\n\n\n(1)创建实体类\n\ncom.ajream.entity.Users\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Users {\n private int id;\n private String name;\n private Area area; //一个用户属于某个地区\n}\n\n```\n\n\n\ncom.ajream.entity.Area\n\n```java\npackage com.ajream.entity;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Area {\n private int id;\n private String name;\n private List users; //一个地区会有多个用户, 即一对多\n}\n\n```\n\n\n\n\n\n(2)使用mybatis进行查询\n\n如果要查询小明是哪个地区的,用sql如何写:\n\n```sql\nselect u.id, u.name, a.name from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n```\n\n![image-20210827205234083](https://gitee.com/ajream/images/raw/master/img/20210827205235_image-20210827205234083.png)\n\n\n\n接着用mybatis实现同样的操作:\n\n\n\n创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Users;\n\npublic interface UserDao {\n Users findByName(String name);\n}\n\n```\n\n\n\n创建mapper映射\n\n```xml\n\n\n\n\n\n \n\n```\n\n\n\n注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n(3)测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.entity.Users;\nimport com.ajream.dao.UserDao;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class UserDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = UserDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n UserDao userDao = sqlSession.getMapper(UserDao.class);\n Users user = userDao.findByName(\"小明\");\n System.out.println(user);\n sqlSession.close();\n\n }\n}\n\n```\n\n输出发现地区是null\n\n![image-20210827212140152](https://gitee.com/ajream/images/raw/master/img/20210827212141_image-20210827212140152.png)\n\n\n\n\n\n这是因为mapper的映射没有做好,实际上,mybatis进行映射时,是通过sql语句的返回结果的字段名来进行映射的\n\n比如这句sql代码返回的结果:\n\n```sql\nselect u.id, u.name, a.name as aname from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n```\n\n![image-20210827220105293](https://gitee.com/ajream/images/raw/master/img/20210827220108_image-20210827220105293.png)\n\n字段名有 `id` 、`name`、`aname` 三个\n\n而我们的实体类 Users\n\n```java\npublic class Users {\n private int id;\n private String name;\n private Area area; //一个用户属于某个地区\n}\n```\n\n也有三个属性 `id`, `name`, `area`\n\n因此 进行映射时 `id` 、`name` 都没有问题,而 `area` 无法与 `aname` 进行映射,所以查询结果是 null\n\n\n\n\n\n因此,关键是要做好 `area` 的映射\n\n在mybatis中,使用resultMap标签来处理这种情况\n\n\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n![image-20210827222054544](https://gitee.com/ajream/images/raw/master/img/20210827222057_image-20210827222054544.png)\n\n解释:\n\n`column` 表示字段名,`property`表示对应的实体类(如上面是 `Users`)的属性名\n\n\n\n![image-20210827220105293](https://gitee.com/ajream/images/raw/master/img/20210827220108_image-20210827220105293.png)\n\n如果是复杂类型的属性,如 `area` 的类型是 `Area`,使用 `` 标签,分别(当然,查询了哪个就映射哪个)对里面的属性进行映射\n\n\n\n\n\n\n\n最后测试运行结果如下:\n\n![image-20210827222509289](https://gitee.com/ajream/images/raw/master/img/20210827222510_image-20210827222509289.png)\n\n\n\n\n\n---\n\n\n\n### 多对一\n\n[项目地址mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master3)\n\n上面演示了一对多,即从用户的方面查询出用户是哪个地区的,\n\n接下来演示了多对一的情形,\"多\"即地区,\"一\"即用户个人,所以多对一是查询一个地区的所有用户\n\n\n\n(1)创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Area;\n\npublic interface AreaDao {\n Area findByName(String name);\n}\n\n```\n\n\n\n(2)创建mapper映射\n\n```xml\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n\n```\n\n说明:多对一关系,使用collection标签, ofType表示集合里面的元素类型\n\n\n\n\n\n(3)注册mapper\n\n\n\n![image-20210827232423407](https://gitee.com/ajream/images/raw/master/img/20210827232426_image-20210827232423407.png)\n\n\n\n(4)测试\n\ncom.ajream.test.AreaDaoTest\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AreaDao;\nimport com.ajream.dao.UserDao;\nimport com.ajream.entity.Area;\nimport com.ajream.entity.Users;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class AreaDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = AreaDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n AreaDao areaDao = sqlSession.getMapper(AreaDao.class);\n Area area = areaDao.findByName(\"华南\");\n System.out.println(area);\n sqlSession.close();\n }\n}\n\n```\n\n\n\n测试结果\n\n![image-20210827232632940](https://gitee.com/ajream/images/raw/master/img/20210827232634_image-20210827232632940.png)\n\n\n\n\n\n### 多对多\n\n[项目地址(某个用户购买了多本书) mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master4)\n\n举个例子,以客户买书为例子,书店有很多种书,语文书、数学书、英语书...,一种书可以被多个客户购买,客户也可以借阅多种书\n\n创建3个表,customer表,books表,books和customer相互映射的表(表示某个用户购买的书类型&某种书被哪些用户购买了)\n\n\n\n```sql\ncreate table customer(\n id int primary key auto_increment,\n name varchar(11)\n);\n\ncreate table books(\n id int primary key auto_increment,\n book_name varchar(11)\n);\ncreate table customer_books(\n id int primary key auto_increment,\n cid int,\n bid int\n);\n\ninsert into customer(id, name) VALUES (1, \"小方\"), (2, \"小海\");\ninsert into books(id, book_name) VALUES (1, \"语文\"), (2,\"数学\"), (3, \"英语\");\ninsert into customer_books(id, cid, bid) values (1,1,1), (2,1,2),(3,1,3),(4,2,1),(5,2,2);\n```\n\n\n\n| customer | books | customer_books(小方买了语数英,小海买了语数) |\n| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210828110017226](https://gitee.com/ajream/images/raw/master/img/20210828110018_image-20210828110017226.png) | ![image-20210828105957247](https://gitee.com/ajream/images/raw/master/img/20210828105958_image-20210828105957247.png) | ![image-20210828105921198](https://gitee.com/ajream/images/raw/master/img/20210828105924_image-20210828105921198.png) |\n\n\n\n查找小方买了那些书\n\n```sql\nselect c.id, c.name, b.book_name \nfrom customer c, books b, customer_books cb \nwhere c.name=\"小方\" and c.id=cb.cid and b.id=cb.bid;\n```\n\n\n\n查找买了语文书的有哪些人\n\n\n\n```sql\nselect c.id, c.name, b.book_name \nfrom customer c, books b, customer_books cb \nwhere b.book_name=\"语文\" and c.id=cb.cid and b.id=cb.bid;\n```\n\n\n\n查找结果如下:\n\n| 小方买的书 | 谁买了语文书 |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210828111314291](https://gitee.com/ajream/images/raw/master/img/20210828111317_image-20210828111314291.png) | ![image-20210828111406936](https://gitee.com/ajream/images/raw/master/img/20210828111408_image-20210828111406936.png) |\n\n\n\n如何使用mybatis实现这样的查询?\n\n\n\n(1)环境配置一条龙\n\n实体类\n\ncom.ajream.entity.Books\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Books {\n private int id;\n private String bookName;\n private List customers;\n}\n\n```\n\ncom.ajream.entity.Customer\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Customer {\n private int id;\n private String name;\n private List books;\n}\n\n```\n\n\n\n(2)如何实现mapper映射\n\n\n\n> 查询某某购买了哪些书\n\n注意,返回结果是某某这个人(即Customer, 拥有id,name,books属性),而不是书(Books)\n\n因此,我们的接口命名为CustomerDao,而不是BooksDao\n\n\n\n接口:\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Customer;\n\npublic interface CustomerDao {\n Customer findByName(String name); //查找某某购买了哪些书\n}\n\n```\n\n\n\nmapper映射\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.CustomerDao;\nimport com.ajream.entity.Customer;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class CustomerDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);\n Customer customer = customerDao.findByName(\"小方\");\n System.out.println(customer);\n\n Customer customer1 = customerDao.findByName(\"小海\");\n System.out.println(customer1);\n sqlSession.close();\n }\n}\n\n```\n\n\n\n输出结果:\n\n![image-20210828120305737](https://gitee.com/ajream/images/raw/master/img/20210828120308_image-20210828120305737.png)\n\n\n\n\n\n> 同理,查找谁购买了XXX这本书也是这样\n\n[项目地址(谁购买了XXX这本书)mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master5)\n\n\n\n创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Books;\n\npublic interface BookDao {\n Books findByBookName(String bookName); //返回结果是一本书\n}\n\n```\n\n\n\n创建mapper\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n注册mapper\n\n```\n......\n```\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.BookDao;\nimport com.ajream.entity.Books;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class BookDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n BookDao bookDao = sqlSession.getMapper(BookDao.class);\n Books book = bookDao.findByBookName(\"语文\");\n System.out.println(book);\n\n Books book1 = bookDao.findByBookName(\"数学\");\n System.out.println(book1);\n\n Books book2 = bookDao.findByBookName(\"英语\");\n System.out.println(book2);\n\n sqlSession.close();\n }\n}\n\n```\n\n\n\n输出结果\n\n\n\n![image-20210828122430777](https://gitee.com/ajream/images/raw/master/img/20210828122433_image-20210828122430777.png)\n\n","source":"_posts/Mybatis/mybatis关联查询(四).md","raw":"---\ntitle: mybatis关联查询(四)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: 多个表之间如何实现关联查询(一对多、多对一、多对多)\nabbrlink: ba43a8dc\ndate: 2021-08-26 09:12:46\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n## 关联查询\n\n关联查询分为一对多、多对一、多对多的情形,这里\"一\"指的是个别、个例,\"多\"指的是集体\n\n### 一对多\n\n比如一个班级有多个学生,一个学生只对应一个班级,现在有2张表,一张是班级表,一张是学生表,要查询一个学生对应的班级,即一对多(学生是单个,但班级是一个集合)\n\n下面以用户和地区(一个用户对应一个地区,一个地区有多名用户)两张表来进行联表查询:查找某个用户所在的地区\n\n\n\n[项目地址mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master2)\n\n\n{% note info flat %}\n看这篇文章前要先了解关系型数据库的 主键外键\n{% endnote %}\n\n先创建2个表\n\n```sql\nuse mybatis;\n\n# 创建2个表\ncreate table users(\n id int primary key auto_increment,\n name varchar(11) not null\n);\n\ncreate table area(\n id int primary key auto_increment,\n name varchar(11)\n);\n\n\n-- 给users表添加外键\nalter table users add aid int;\nalter table users add constraint area_id foreign key (aid) references area (id);\n\n\n# 插入数据\ninsert into area(id, name) VALUES (2, \"华南\");\ninsert into area(id, name) VALUES (3, \"东北\");\ninsert into area(id, name) VALUES (9, \"华北\");\n\ninsert into users(id, name, aid) VALUES (1, \"小明\", 2);\ninsert into users(id, name, aid) VALUES (2, \"小王\", 9);\ninsert into users(id, name, aid) VALUES (3, \"小虹\", 2);\ninsert into users(id, name, aid) VALUES (4, \"小天\", 3);\n\n\n\nselect u.id, u.name, a.name from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n\n```\n\n\n\n| users | area |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210827202630533](https://gitee.com/ajream/images/raw/master/img/20210827202634_image-20210827202630533.png) | ![image-20210827211945478](https://gitee.com/ajream/images/raw/master/img/20210827211948_image-20210827211945478.png) |\n\n\n\n\n\n#### 环境配置\n\n\n\n导入包 + resources配置\n\n```xml\n\n \n mysql\n mysql-connector-java\n 8.0.26\n \n \n org.mybatis\n mybatis\n 3.5.6\n \n \n org.projectlombok\n lombok\n 1.18.20\n \n \n junit\n junit\n 4.13\n test\n \n\n\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\nmybatis-config.xml\n\n\n\n```xml\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n\n```\n\n\n\n\n\n#### 进行开发\n\n\n\n(1)创建实体类\n\ncom.ajream.entity.Users\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Users {\n private int id;\n private String name;\n private Area area; //一个用户属于某个地区\n}\n\n```\n\n\n\ncom.ajream.entity.Area\n\n```java\npackage com.ajream.entity;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Area {\n private int id;\n private String name;\n private List users; //一个地区会有多个用户, 即一对多\n}\n\n```\n\n\n\n\n\n(2)使用mybatis进行查询\n\n如果要查询小明是哪个地区的,用sql如何写:\n\n```sql\nselect u.id, u.name, a.name from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n```\n\n![image-20210827205234083](https://gitee.com/ajream/images/raw/master/img/20210827205235_image-20210827205234083.png)\n\n\n\n接着用mybatis实现同样的操作:\n\n\n\n创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Users;\n\npublic interface UserDao {\n Users findByName(String name);\n}\n\n```\n\n\n\n创建mapper映射\n\n```xml\n\n\n\n\n\n \n\n```\n\n\n\n注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n(3)测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.entity.Users;\nimport com.ajream.dao.UserDao;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class UserDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = UserDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n UserDao userDao = sqlSession.getMapper(UserDao.class);\n Users user = userDao.findByName(\"小明\");\n System.out.println(user);\n sqlSession.close();\n\n }\n}\n\n```\n\n输出发现地区是null\n\n![image-20210827212140152](https://gitee.com/ajream/images/raw/master/img/20210827212141_image-20210827212140152.png)\n\n\n\n\n\n这是因为mapper的映射没有做好,实际上,mybatis进行映射时,是通过sql语句的返回结果的字段名来进行映射的\n\n比如这句sql代码返回的结果:\n\n```sql\nselect u.id, u.name, a.name as aname from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n```\n\n![image-20210827220105293](https://gitee.com/ajream/images/raw/master/img/20210827220108_image-20210827220105293.png)\n\n字段名有 `id` 、`name`、`aname` 三个\n\n而我们的实体类 Users\n\n```java\npublic class Users {\n private int id;\n private String name;\n private Area area; //一个用户属于某个地区\n}\n```\n\n也有三个属性 `id`, `name`, `area`\n\n因此 进行映射时 `id` 、`name` 都没有问题,而 `area` 无法与 `aname` 进行映射,所以查询结果是 null\n\n\n\n\n\n因此,关键是要做好 `area` 的映射\n\n在mybatis中,使用resultMap标签来处理这种情况\n\n\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n![image-20210827222054544](https://gitee.com/ajream/images/raw/master/img/20210827222057_image-20210827222054544.png)\n\n解释:\n\n`column` 表示字段名,`property`表示对应的实体类(如上面是 `Users`)的属性名\n\n\n\n![image-20210827220105293](https://gitee.com/ajream/images/raw/master/img/20210827220108_image-20210827220105293.png)\n\n如果是复杂类型的属性,如 `area` 的类型是 `Area`,使用 `` 标签,分别(当然,查询了哪个就映射哪个)对里面的属性进行映射\n\n\n\n\n\n\n\n最后测试运行结果如下:\n\n![image-20210827222509289](https://gitee.com/ajream/images/raw/master/img/20210827222510_image-20210827222509289.png)\n\n\n\n\n\n---\n\n\n\n### 多对一\n\n[项目地址mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master3)\n\n上面演示了一对多,即从用户的方面查询出用户是哪个地区的,\n\n接下来演示了多对一的情形,\"多\"即地区,\"一\"即用户个人,所以多对一是查询一个地区的所有用户\n\n\n\n(1)创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Area;\n\npublic interface AreaDao {\n Area findByName(String name);\n}\n\n```\n\n\n\n(2)创建mapper映射\n\n```xml\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n\n```\n\n说明:多对一关系,使用collection标签, ofType表示集合里面的元素类型\n\n\n\n\n\n(3)注册mapper\n\n\n\n![image-20210827232423407](https://gitee.com/ajream/images/raw/master/img/20210827232426_image-20210827232423407.png)\n\n\n\n(4)测试\n\ncom.ajream.test.AreaDaoTest\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AreaDao;\nimport com.ajream.dao.UserDao;\nimport com.ajream.entity.Area;\nimport com.ajream.entity.Users;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class AreaDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = AreaDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n AreaDao areaDao = sqlSession.getMapper(AreaDao.class);\n Area area = areaDao.findByName(\"华南\");\n System.out.println(area);\n sqlSession.close();\n }\n}\n\n```\n\n\n\n测试结果\n\n![image-20210827232632940](https://gitee.com/ajream/images/raw/master/img/20210827232634_image-20210827232632940.png)\n\n\n\n\n\n### 多对多\n\n[项目地址(某个用户购买了多本书) mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master4)\n\n举个例子,以客户买书为例子,书店有很多种书,语文书、数学书、英语书...,一种书可以被多个客户购买,客户也可以借阅多种书\n\n创建3个表,customer表,books表,books和customer相互映射的表(表示某个用户购买的书类型&某种书被哪些用户购买了)\n\n\n\n```sql\ncreate table customer(\n id int primary key auto_increment,\n name varchar(11)\n);\n\ncreate table books(\n id int primary key auto_increment,\n book_name varchar(11)\n);\ncreate table customer_books(\n id int primary key auto_increment,\n cid int,\n bid int\n);\n\ninsert into customer(id, name) VALUES (1, \"小方\"), (2, \"小海\");\ninsert into books(id, book_name) VALUES (1, \"语文\"), (2,\"数学\"), (3, \"英语\");\ninsert into customer_books(id, cid, bid) values (1,1,1), (2,1,2),(3,1,3),(4,2,1),(5,2,2);\n```\n\n\n\n| customer | books | customer_books(小方买了语数英,小海买了语数) |\n| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210828110017226](https://gitee.com/ajream/images/raw/master/img/20210828110018_image-20210828110017226.png) | ![image-20210828105957247](https://gitee.com/ajream/images/raw/master/img/20210828105958_image-20210828105957247.png) | ![image-20210828105921198](https://gitee.com/ajream/images/raw/master/img/20210828105924_image-20210828105921198.png) |\n\n\n\n查找小方买了那些书\n\n```sql\nselect c.id, c.name, b.book_name \nfrom customer c, books b, customer_books cb \nwhere c.name=\"小方\" and c.id=cb.cid and b.id=cb.bid;\n```\n\n\n\n查找买了语文书的有哪些人\n\n\n\n```sql\nselect c.id, c.name, b.book_name \nfrom customer c, books b, customer_books cb \nwhere b.book_name=\"语文\" and c.id=cb.cid and b.id=cb.bid;\n```\n\n\n\n查找结果如下:\n\n| 小方买的书 | 谁买了语文书 |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210828111314291](https://gitee.com/ajream/images/raw/master/img/20210828111317_image-20210828111314291.png) | ![image-20210828111406936](https://gitee.com/ajream/images/raw/master/img/20210828111408_image-20210828111406936.png) |\n\n\n\n如何使用mybatis实现这样的查询?\n\n\n\n(1)环境配置一条龙\n\n实体类\n\ncom.ajream.entity.Books\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Books {\n private int id;\n private String bookName;\n private List customers;\n}\n\n```\n\ncom.ajream.entity.Customer\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Customer {\n private int id;\n private String name;\n private List books;\n}\n\n```\n\n\n\n(2)如何实现mapper映射\n\n\n\n> 查询某某购买了哪些书\n\n注意,返回结果是某某这个人(即Customer, 拥有id,name,books属性),而不是书(Books)\n\n因此,我们的接口命名为CustomerDao,而不是BooksDao\n\n\n\n接口:\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Customer;\n\npublic interface CustomerDao {\n Customer findByName(String name); //查找某某购买了哪些书\n}\n\n```\n\n\n\nmapper映射\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.CustomerDao;\nimport com.ajream.entity.Customer;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class CustomerDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);\n Customer customer = customerDao.findByName(\"小方\");\n System.out.println(customer);\n\n Customer customer1 = customerDao.findByName(\"小海\");\n System.out.println(customer1);\n sqlSession.close();\n }\n}\n\n```\n\n\n\n输出结果:\n\n![image-20210828120305737](https://gitee.com/ajream/images/raw/master/img/20210828120308_image-20210828120305737.png)\n\n\n\n\n\n> 同理,查找谁购买了XXX这本书也是这样\n\n[项目地址(谁购买了XXX这本书)mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master5)\n\n\n\n创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Books;\n\npublic interface BookDao {\n Books findByBookName(String bookName); //返回结果是一本书\n}\n\n```\n\n\n\n创建mapper\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n注册mapper\n\n```\n......\n```\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.BookDao;\nimport com.ajream.entity.Books;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class BookDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n BookDao bookDao = sqlSession.getMapper(BookDao.class);\n Books book = bookDao.findByBookName(\"语文\");\n System.out.println(book);\n\n Books book1 = bookDao.findByBookName(\"数学\");\n System.out.println(book1);\n\n Books book2 = bookDao.findByBookName(\"英语\");\n System.out.println(book2);\n\n sqlSession.close();\n }\n}\n\n```\n\n\n\n输出结果\n\n\n\n![image-20210828122430777](https://gitee.com/ajream/images/raw/master/img/20210828122433_image-20210828122430777.png)\n\n","slug":"Mybatis/mybatis关联查询(四)","published":1,"updated":"2021-08-30T09:02:15.636Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6tv00h5akvefji7akri","content":"

关联查询

关联查询分为一对多、多对一、多对多的情形,这里”一”指的是个别、个例,”多”指的是集体

\n

一对多

比如一个班级有多个学生,一个学生只对应一个班级,现在有2张表,一张是班级表,一张是学生表,要查询一个学生对应的班级,即一对多(学生是单个,但班级是一个集合)

\n

下面以用户和地区(一个用户对应一个地区,一个地区有多名用户)两张表来进行联表查询:查找某个用户所在的地区

\n

项目地址mybatis-04

\n

看这篇文章前要先了解关系型数据库的 主键外键

\n
\n

先创建2个表

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use mybatis;

# 创建2个表
create table users(
id int primary key auto_increment,
name varchar(11) not null
);

create table area(
id int primary key auto_increment,
name varchar(11)
);


-- 给users表添加外键
alter table users add aid int;
alter table users add constraint area_id foreign key (aid) references area (id);


# 插入数据
insert into area(id, name) VALUES (2, "华南");
insert into area(id, name) VALUES (3, "东北");
insert into area(id, name) VALUES (9, "华北");

insert into users(id, name, aid) VALUES (1, "小明", 2);
insert into users(id, name, aid) VALUES (2, "小王", 9);
insert into users(id, name, aid) VALUES (3, "小虹", 2);
insert into users(id, name, aid) VALUES (4, "小天", 3);



select u.id, u.name, a.name from users u, area a where u.name="小明" and u.aid = a.id ;

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
usersarea
\"image-20210827202630533\"\"image-20210827211945478\"
\n
\n

环境配置

导入包 + resources配置

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

mybatis-config.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!-- 2. 配置mybatis运行环境,可以有多个运行环境,default表示默认的运行环境是...,id是每个运行环境的唯一标识-->
<environments default="development">
<environment id="development">
<!-- 3. 配置JDBC事务管理-->
<transactionManager type="JDBC" />
<!-- 4. 配置JDBC数据源连接池POOLED-->
<dataSource type="POOLED">
<!-- 5. 配置数据库链接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--配置驱动-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>

</environment>
</environments>
</configuration>
\n

进行开发

(1)创建实体类

\n

com.ajream.entity.Users

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
private int id;
private String name;
private Area area; //一个用户属于某个地区
}

\n

com.ajream.entity.Area

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ajream.entity;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Area {
private int id;
private String name;
private List<Users> users; //一个地区会有多个用户, 即一对多
}

\n

(2)使用mybatis进行查询

\n

如果要查询小明是哪个地区的,用sql如何写:

\n
1
select u.id, u.name, a.name from users u, area a where u.name="小明" and u.aid = a.id ;
\n

\"image-20210827205234083\"

\n

接着用mybatis实现同样的操作:

\n

创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Users;

public interface UserDao {
Users findByName(String name);
}

\n

创建mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.UserDao">
<select id="findByName" parameterType="java.lang.String" resultType="com.ajream.entity.Users">
select u.id, u.name, a.name as aname from users u, area a where u.name="小明" and u.aid = a.id ;
</select>
</mapper>
\n

注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/UserDao.xml"/>
</mappers>
\n

(3)测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.ajream.test;

import com.ajream.entity.Users;
import com.ajream.dao.UserDao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class UserDaoTest {
public static void main(String[] args) {
InputStream inputStream = UserDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

UserDao userDao = sqlSession.getMapper(UserDao.class);
Users user = userDao.findByName("小明");
System.out.println(user);
sqlSession.close();

}
}

\n

输出发现地区是null

\n

\"image-20210827212140152\"

\n

这是因为mapper的映射没有做好,实际上,mybatis进行映射时,是通过sql语句的返回结果的字段名来进行映射的

\n

比如这句sql代码返回的结果:

\n
1
select u.id, u.name, a.name as aname from users u, area a where u.name="小明" and u.aid = a.id ;
\n

\"image-20210827220105293\"

\n

字段名有 idnameaname 三个

\n

而我们的实体类 Users

\n
1
2
3
4
5
public class Users {
private int id;
private String name;
private Area area; //一个用户属于某个地区
}
\n

也有三个属性 idnamearea

\n

因此 进行映射时 idname 都没有问题,而 area 无法与 aname 进行映射,所以查询结果是 null

\n

因此,关键是要做好 area 的映射

\n

在mybatis中,使用resultMap标签来处理这种情况

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.UserDao">

<resultMap id="resultUser" type="com.ajream.entity.Users">
<id column="id" property="id"/> <!--主键映射用<id>标签-->
<result column="name" property="name"/> <!--其他键映射都用<result>标签-->
<association property="area" javaType="com.ajream.entity.Area">
<result column="aname" property="name"/>
</association>
</resultMap>

<select id="findByName" parameterType="java.lang.String" resultMap="resultUser"> <!--不使用resultType, 改用resultMap-->
select u.id, u.name, a.name as aname from users u, area a where u.name=#{name} and u.aid = a.id ;
</select>
</mapper>
\n

\"image-20210827222054544\"

\n

解释:

\n

column 表示字段名,property表示对应的实体类(如上面是 Users)的属性名

\n

\"image-20210827220105293\"

\n

如果是复杂类型的属性,如 area 的类型是 Area,使用 <association> 标签,分别(当然,查询了哪个就映射哪个)对里面的属性进行映射

\n

最后测试运行结果如下:

\n

\"image-20210827222509289\"

\n
\n

多对一

项目地址mybatis-04

\n

上面演示了一对多,即从用户的方面查询出用户是哪个地区的,

\n

接下来演示了多对一的情形,”多”即地区,”一”即用户个人,所以多对一是查询一个地区的所有用户

\n

(1)创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Area;

public interface AreaDao {
Area findByName(String name);
}

\n

(2)创建mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.AreaDao">
<resultMap id="resultArea" type="com.ajream.entity.Area">
<result column="aname" property="name"/> <!--除了主键,其他键映射都用<result>标签-->

<collection property="users" ofType="com.ajream.entity.Users"> <!--使用collection标签, ofType表示集合里面的元素类型-->
<id column="id" property="id"/>
<result column="name" property="name"/>
</collection>
</resultMap>
<select id="findByName" parameterType="java.lang.String" resultMap="resultArea">
select u.id, u.name, a.name as aname from users u, area a where a.name=#{name} and u.aid = a.id;
</select>
</mapper>
\n

说明:多对一关系,使用collection标签, ofType表示集合里面的元素类型

\n

(3)注册mapper

\n

\"image-20210827232423407\"

\n

(4)测试

\n

com.ajream.test.AreaDaoTest

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.ajream.test;

import com.ajream.dao.AreaDao;
import com.ajream.dao.UserDao;
import com.ajream.entity.Area;
import com.ajream.entity.Users;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class AreaDaoTest {
public static void main(String[] args) {
InputStream inputStream = AreaDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

AreaDao areaDao = sqlSession.getMapper(AreaDao.class);
Area area = areaDao.findByName("华南");
System.out.println(area);
sqlSession.close();
}
}

\n

测试结果

\n

\"image-20210827232632940\"

\n

多对多

项目地址(某个用户购买了多本书) mybatis-04

\n

举个例子,以客户买书为例子,书店有很多种书,语文书、数学书、英语书…,一种书可以被多个客户购买,客户也可以借阅多种书

\n

创建3个表,customer表,books表,books和customer相互映射的表(表示某个用户购买的书类型&某种书被哪些用户购买了)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
create table customer(
id int primary key auto_increment,
name varchar(11)
);

create table books(
id int primary key auto_increment,
book_name varchar(11)
);
create table customer_books(
id int primary key auto_increment,
cid int,
bid int
);

insert into customer(id, name) VALUES (1, "小方"), (2, "小海");
insert into books(id, book_name) VALUES (1, "语文"), (2,"数学"), (3, "英语");
insert into customer_books(id, cid, bid) values (1,1,1), (2,1,2),(3,1,3),(4,2,1),(5,2,2);
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
customerbookscustomer_books(小方买了语数英,小海买了语数)
\"image-20210828110017226\"\"image-20210828105957247\"\"image-20210828105921198\"
\n
\n

查找小方买了那些书

\n
1
2
3
select c.id, c.name, b.book_name 
from customer c, books b, customer_books cb
where c.name="小方" and c.id=cb.cid and b.id=cb.bid;
\n

查找买了语文书的有哪些人

\n
1
2
3
select c.id, c.name, b.book_name 
from customer c, books b, customer_books cb
where b.book_name="语文" and c.id=cb.cid and b.id=cb.bid;
\n

查找结果如下:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
小方买的书谁买了语文书
\"image-20210828111314291\"\"image-20210828111406936\"
\n
\n

如何使用mybatis实现这样的查询?

\n

(1)环境配置一条龙

\n

实体类

\n

com.ajream.entity.Books

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {
private int id;
private String bookName;
private List<Customer> customers;
}

\n

com.ajream.entity.Customer

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Customer {
private int id;
private String name;
private List<Books> books;
}

\n

(2)如何实现mapper映射

\n
\n

查询某某购买了哪些书

\n
\n

注意,返回结果是某某这个人(即Customer, 拥有id,name,books属性),而不是书(Books)

\n

因此,我们的接口命名为CustomerDao,而不是BooksDao

\n

接口:

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Customer;

public interface CustomerDao {
Customer findByName(String name); //查找某某购买了哪些书
}

\n

mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.CustomerDao">
<resultMap id="resultCustomer" type="com.ajream.entity.Customer"> <!--返回结果是某某,所以type是Customer-->
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="books" ofType="com.ajream.entity.Books"> <!--使用collections标签-->
<result column="book_name" property="bookName"/>
</collection>
</resultMap>
<select id="findByName" parameterType="java.lang.String" resultMap="resultCustomer">
select c.id, c.name, b.book_name
from customer c, books b, customer_books cb
where c.name=#{name} and c.id=cb.cid and b.id=cb.bid;
</select>
</mapper>
\n

注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/CustomerDao.xml"/>
</mappers>
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.ajream.test;

import com.ajream.dao.CustomerDao;
import com.ajream.entity.Customer;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class CustomerDaoTest {
public static void main(String[] args) {
InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);
Customer customer = customerDao.findByName("小方");
System.out.println(customer);

Customer customer1 = customerDao.findByName("小海");
System.out.println(customer1);
sqlSession.close();
}
}

\n

输出结果:

\n

\"image-20210828120305737\"

\n
\n

同理,查找谁购买了XXX这本书也是这样

\n
\n

项目地址(谁购买了XXX这本书)mybatis-04

\n

创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Books;

public interface BookDao {
Books findByBookName(String bookName); //返回结果是一本书
}

\n

创建mapper

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.BookDao">
<resultMap id="resultBook" type="com.ajream.entity.Books">
<result column="book_name" property="bookName"/>
<collection property="customers" ofType="com.ajream.entity.Customer">
<id column="id" property="id"/>
<result column="name" property="name"/>
</collection>
</resultMap>
<select id="findByBookName" resultMap="resultBook">
select c.id, c.name, b.book_name
from customer c, books b, customer_books cb
where b.book_name=#{bookName} and c.id=cb.cid and b.id=cb.bid;
</select>
</mapper>
\n

注册mapper

\n
1
......
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.ajream.test;

import com.ajream.dao.BookDao;
import com.ajream.entity.Books;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class BookDaoTest {
public static void main(String[] args) {
InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

BookDao bookDao = sqlSession.getMapper(BookDao.class);
Books book = bookDao.findByBookName("语文");
System.out.println(book);

Books book1 = bookDao.findByBookName("数学");
System.out.println(book1);

Books book2 = bookDao.findByBookName("英语");
System.out.println(book2);

sqlSession.close();
}
}

\n

输出结果

\n

\"image-20210828122430777\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

关联查询

关联查询分为一对多、多对一、多对多的情形,这里”一”指的是个别、个例,”多”指的是集体

\n

一对多

比如一个班级有多个学生,一个学生只对应一个班级,现在有2张表,一张是班级表,一张是学生表,要查询一个学生对应的班级,即一对多(学生是单个,但班级是一个集合)

\n

下面以用户和地区(一个用户对应一个地区,一个地区有多名用户)两张表来进行联表查询:查找某个用户所在的地区

\n

项目地址mybatis-04

\n

看这篇文章前要先了解关系型数据库的 主键外键

\n
\n

先创建2个表

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use mybatis;

# 创建2个表
create table users(
id int primary key auto_increment,
name varchar(11) not null
);

create table area(
id int primary key auto_increment,
name varchar(11)
);


-- 给users表添加外键
alter table users add aid int;
alter table users add constraint area_id foreign key (aid) references area (id);


# 插入数据
insert into area(id, name) VALUES (2, "华南");
insert into area(id, name) VALUES (3, "东北");
insert into area(id, name) VALUES (9, "华北");

insert into users(id, name, aid) VALUES (1, "小明", 2);
insert into users(id, name, aid) VALUES (2, "小王", 9);
insert into users(id, name, aid) VALUES (3, "小虹", 2);
insert into users(id, name, aid) VALUES (4, "小天", 3);



select u.id, u.name, a.name from users u, area a where u.name="小明" and u.aid = a.id ;

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
usersarea
\"image-20210827202630533\"\"image-20210827211945478\"
\n
\n

环境配置

导入包 + resources配置

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

mybatis-config.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!-- 2. 配置mybatis运行环境,可以有多个运行环境,default表示默认的运行环境是...,id是每个运行环境的唯一标识-->
<environments default="development">
<environment id="development">
<!-- 3. 配置JDBC事务管理-->
<transactionManager type="JDBC" />
<!-- 4. 配置JDBC数据源连接池POOLED-->
<dataSource type="POOLED">
<!-- 5. 配置数据库链接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--配置驱动-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>

</environment>
</environments>
</configuration>
\n

进行开发

(1)创建实体类

\n

com.ajream.entity.Users

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
private int id;
private String name;
private Area area; //一个用户属于某个地区
}

\n

com.ajream.entity.Area

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ajream.entity;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Area {
private int id;
private String name;
private List<Users> users; //一个地区会有多个用户, 即一对多
}

\n

(2)使用mybatis进行查询

\n

如果要查询小明是哪个地区的,用sql如何写:

\n
1
select u.id, u.name, a.name from users u, area a where u.name="小明" and u.aid = a.id ;
\n

\"image-20210827205234083\"

\n

接着用mybatis实现同样的操作:

\n

创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Users;

public interface UserDao {
Users findByName(String name);
}

\n

创建mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.UserDao">
<select id="findByName" parameterType="java.lang.String" resultType="com.ajream.entity.Users">
select u.id, u.name, a.name as aname from users u, area a where u.name="小明" and u.aid = a.id ;
</select>
</mapper>
\n

注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/UserDao.xml"/>
</mappers>
\n

(3)测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.ajream.test;

import com.ajream.entity.Users;
import com.ajream.dao.UserDao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class UserDaoTest {
public static void main(String[] args) {
InputStream inputStream = UserDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

UserDao userDao = sqlSession.getMapper(UserDao.class);
Users user = userDao.findByName("小明");
System.out.println(user);
sqlSession.close();

}
}

\n

输出发现地区是null

\n

\"image-20210827212140152\"

\n

这是因为mapper的映射没有做好,实际上,mybatis进行映射时,是通过sql语句的返回结果的字段名来进行映射的

\n

比如这句sql代码返回的结果:

\n
1
select u.id, u.name, a.name as aname from users u, area a where u.name="小明" and u.aid = a.id ;
\n

\"image-20210827220105293\"

\n

字段名有 idnameaname 三个

\n

而我们的实体类 Users

\n
1
2
3
4
5
public class Users {
private int id;
private String name;
private Area area; //一个用户属于某个地区
}
\n

也有三个属性 idnamearea

\n

因此 进行映射时 idname 都没有问题,而 area 无法与 aname 进行映射,所以查询结果是 null

\n

因此,关键是要做好 area 的映射

\n

在mybatis中,使用resultMap标签来处理这种情况

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.UserDao">

<resultMap id="resultUser" type="com.ajream.entity.Users">
<id column="id" property="id"/> <!--主键映射用<id>标签-->
<result column="name" property="name"/> <!--其他键映射都用<result>标签-->
<association property="area" javaType="com.ajream.entity.Area">
<result column="aname" property="name"/>
</association>
</resultMap>

<select id="findByName" parameterType="java.lang.String" resultMap="resultUser"> <!--不使用resultType, 改用resultMap-->
select u.id, u.name, a.name as aname from users u, area a where u.name=#{name} and u.aid = a.id ;
</select>
</mapper>
\n

\"image-20210827222054544\"

\n

解释:

\n

column 表示字段名,property表示对应的实体类(如上面是 Users)的属性名

\n

\"image-20210827220105293\"

\n

如果是复杂类型的属性,如 area 的类型是 Area,使用 <association> 标签,分别(当然,查询了哪个就映射哪个)对里面的属性进行映射

\n

最后测试运行结果如下:

\n

\"image-20210827222509289\"

\n
\n

多对一

项目地址mybatis-04

\n

上面演示了一对多,即从用户的方面查询出用户是哪个地区的,

\n

接下来演示了多对一的情形,”多”即地区,”一”即用户个人,所以多对一是查询一个地区的所有用户

\n

(1)创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Area;

public interface AreaDao {
Area findByName(String name);
}

\n

(2)创建mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.AreaDao">
<resultMap id="resultArea" type="com.ajream.entity.Area">
<result column="aname" property="name"/> <!--除了主键,其他键映射都用<result>标签-->

<collection property="users" ofType="com.ajream.entity.Users"> <!--使用collection标签, ofType表示集合里面的元素类型-->
<id column="id" property="id"/>
<result column="name" property="name"/>
</collection>
</resultMap>
<select id="findByName" parameterType="java.lang.String" resultMap="resultArea">
select u.id, u.name, a.name as aname from users u, area a where a.name=#{name} and u.aid = a.id;
</select>
</mapper>
\n

说明:多对一关系,使用collection标签, ofType表示集合里面的元素类型

\n

(3)注册mapper

\n

\"image-20210827232423407\"

\n

(4)测试

\n

com.ajream.test.AreaDaoTest

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.ajream.test;

import com.ajream.dao.AreaDao;
import com.ajream.dao.UserDao;
import com.ajream.entity.Area;
import com.ajream.entity.Users;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class AreaDaoTest {
public static void main(String[] args) {
InputStream inputStream = AreaDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

AreaDao areaDao = sqlSession.getMapper(AreaDao.class);
Area area = areaDao.findByName("华南");
System.out.println(area);
sqlSession.close();
}
}

\n

测试结果

\n

\"image-20210827232632940\"

\n

多对多

项目地址(某个用户购买了多本书) mybatis-04

\n

举个例子,以客户买书为例子,书店有很多种书,语文书、数学书、英语书…,一种书可以被多个客户购买,客户也可以借阅多种书

\n

创建3个表,customer表,books表,books和customer相互映射的表(表示某个用户购买的书类型&某种书被哪些用户购买了)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
create table customer(
id int primary key auto_increment,
name varchar(11)
);

create table books(
id int primary key auto_increment,
book_name varchar(11)
);
create table customer_books(
id int primary key auto_increment,
cid int,
bid int
);

insert into customer(id, name) VALUES (1, "小方"), (2, "小海");
insert into books(id, book_name) VALUES (1, "语文"), (2,"数学"), (3, "英语");
insert into customer_books(id, cid, bid) values (1,1,1), (2,1,2),(3,1,3),(4,2,1),(5,2,2);
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
customerbookscustomer_books(小方买了语数英,小海买了语数)
\"image-20210828110017226\"\"image-20210828105957247\"\"image-20210828105921198\"
\n
\n

查找小方买了那些书

\n
1
2
3
select c.id, c.name, b.book_name 
from customer c, books b, customer_books cb
where c.name="小方" and c.id=cb.cid and b.id=cb.bid;
\n

查找买了语文书的有哪些人

\n
1
2
3
select c.id, c.name, b.book_name 
from customer c, books b, customer_books cb
where b.book_name="语文" and c.id=cb.cid and b.id=cb.bid;
\n

查找结果如下:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
小方买的书谁买了语文书
\"image-20210828111314291\"\"image-20210828111406936\"
\n
\n

如何使用mybatis实现这样的查询?

\n

(1)环境配置一条龙

\n

实体类

\n

com.ajream.entity.Books

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {
private int id;
private String bookName;
private List<Customer> customers;
}

\n

com.ajream.entity.Customer

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Customer {
private int id;
private String name;
private List<Books> books;
}

\n

(2)如何实现mapper映射

\n
\n

查询某某购买了哪些书

\n
\n

注意,返回结果是某某这个人(即Customer, 拥有id,name,books属性),而不是书(Books)

\n

因此,我们的接口命名为CustomerDao,而不是BooksDao

\n

接口:

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Customer;

public interface CustomerDao {
Customer findByName(String name); //查找某某购买了哪些书
}

\n

mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.CustomerDao">
<resultMap id="resultCustomer" type="com.ajream.entity.Customer"> <!--返回结果是某某,所以type是Customer-->
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="books" ofType="com.ajream.entity.Books"> <!--使用collections标签-->
<result column="book_name" property="bookName"/>
</collection>
</resultMap>
<select id="findByName" parameterType="java.lang.String" resultMap="resultCustomer">
select c.id, c.name, b.book_name
from customer c, books b, customer_books cb
where c.name=#{name} and c.id=cb.cid and b.id=cb.bid;
</select>
</mapper>
\n

注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/CustomerDao.xml"/>
</mappers>
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.ajream.test;

import com.ajream.dao.CustomerDao;
import com.ajream.entity.Customer;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class CustomerDaoTest {
public static void main(String[] args) {
InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);
Customer customer = customerDao.findByName("小方");
System.out.println(customer);

Customer customer1 = customerDao.findByName("小海");
System.out.println(customer1);
sqlSession.close();
}
}

\n

输出结果:

\n

\"image-20210828120305737\"

\n
\n

同理,查找谁购买了XXX这本书也是这样

\n
\n

项目地址(谁购买了XXX这本书)mybatis-04

\n

创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Books;

public interface BookDao {
Books findByBookName(String bookName); //返回结果是一本书
}

\n

创建mapper

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.BookDao">
<resultMap id="resultBook" type="com.ajream.entity.Books">
<result column="book_name" property="bookName"/>
<collection property="customers" ofType="com.ajream.entity.Customer">
<id column="id" property="id"/>
<result column="name" property="name"/>
</collection>
</resultMap>
<select id="findByBookName" resultMap="resultBook">
select c.id, c.name, b.book_name
from customer c, books b, customer_books cb
where b.book_name=#{bookName} and c.id=cb.cid and b.id=cb.bid;
</select>
</mapper>
\n

注册mapper

\n
1
......
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.ajream.test;

import com.ajream.dao.BookDao;
import com.ajream.entity.Books;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class BookDaoTest {
public static void main(String[] args) {
InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

BookDao bookDao = sqlSession.getMapper(BookDao.class);
Books book = bookDao.findByBookName("语文");
System.out.println(book);

Books book1 = bookDao.findByBookName("数学");
System.out.println(book1);

Books book2 = bookDao.findByBookName("英语");
System.out.println(book2);

sqlSession.close();
}
}

\n

输出结果

\n

\"image-20210828122430777\"

\n"},{"title":"14-接口与继承","abbrlink":"ab09b476","date":"2021-02-14T07:38:23.000Z","description":"内容较多,主要是关于类的一些知识:接口和抽象类、子类与父类的关系、内部类等","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 接口与继承\n\n## 接口\n\n### 接口\n\n接口的创建:(与创建类相似)\n\n```java\n//创建一个播放器接口\npublic interface Player{\n public void play();\n public void pause();\n public void stop();\n public void tune(); //接口只有方法声明,没有主体,并且默认都是public的,可以不写“public”\n}\n```\n\nJava接口是一系列方法的**声明**(没有主体),是一些方法特征的集合;\n\n一个接口只有方法的特征**没有方法的实现**(不能使用 new来创建实例),因此这些方法可以在不同的地方被不同的类实现(继承),进而在不同的类中实现不同的功能。\n\n接口就是一组抽象方法和常量值的集合。可以把接口看成是一种特殊的抽象类。\n\n(1)其所有的方法都必须是抽象的(abstract)。\n\n(2)其属性成员(若有)只能是final static的常量。\n\n### 实现接口\n\n实现一个接口与类的继承相似,用 `implements` 来实现\n\n```java\n// 创建一个MP3类,实现播放器功能\npublic MP3 implements Player{\n public void play(){\n System.out.println(\"播放\");\n }\n \n public void pause(){\n System.out.println(\"暂停\");\n }\n \n public void stop(){\n System.out.println(\"停止\");\n }\n \n public void tune(){\n System.out.println(\"调节音量\");\n }\n}\n```\n\n\n\n## 对象转型\n\n### 明确引用类型与对象类型的概念\n\n引用和对象都是有类型的\n\n```java\nHero h = new Hero();\n```\n\n在这个例子,引用是 `h`,对象是 `new Hero()`,它们的类型均为 `Hero`\n\n通常情况引用类型与对象类型是一样的,但也有不一样的时候\n\n### 子类转父类(向上转型)\n\n所谓的转型,是指当**引用类型**和**对象类型**不一致的时候,才需要进行类型转换;\n类型转换有时候会成功,有时候会失败\n\n假如 `ADHero` 是 `Hero` 的子类\n\n```java\nHero h = new Hero();\nADHero ad = new ADHero();\n\n```\n\n用图来表示其关系如下:\n\n\"image-20210126224549229\"\n\n```java\nh = ad; //向上转型\n```\n\n转型后的图:\n\n\"image-20210126224655825\"\n\n可以看到 `h` 经过 `ad` 最终指向的还是 `Hero`\n\n\n\n因此,把 `h` 指向 `ADHero` 一定可以,因为 `ADHero` 继承了 `Hero`,Hero能实现的功能 ADHero 也可以\n\n\n\n### 父类转子类(向下转型)\n\n父类转子类,有的时候行,**有的时候不行**,所以必须进行强制转换。\n强制转换的意思就是:转换有风险,风险自担。\n\n假如 `ADHero` 和 `Support` 是 `Hero` 的两个不同子类:\n\n```java\nHero h =new Hero();\nADHero ad = new ADHero();\nSupport s = new Support(); \n```\n\n```java\nh = s; // 向上转型,一定可以\n```\n\n向上转型一定可以,因为 `h`通过 `s` 最终还是指向 `Hero`\n\n\"image-20210126225703371\"\n\n\n\n```java\nad = (ADHero)h; //向下转型,不一定可以,需要强制转换\n```\n\n虽然 `ad` 指向的 `ADHero` 是 `Hero` 的子类,眨眼看来 `ad` 最终也是指向 Hero 的,但实际上指向的是 ADHero,ADHero与Hero还是有区别的,ADHero有的方法Hero不一定有\n\n而ad通过h最终指向的是 Hero,而不是 ADHero,因此会有风险(指向ADHero才不会有风险),只能强制转换。\n\n\"image-20210126230408061\"\n\n因为子类拥有的方法父类不一定有,因此 `h` 向下转型后,`ad`可能就没有了一些子类的方法\n\n### 没有继承关系的两个类\n\n没有继承关系的两个类,互相转换,一定会失败\n\n\n\n### 实现类转换成接口(向上转型)\n\n假如 `AD` 是一个接口,`ADHero` 类继承了它\n\n```java\npublic class Test {\n public static void main(String[] args) {\n ADHero ad = new ADHero();\n \n AD adi = ad; //向上转型 \n } \n}\n```\n\n\n\n### 转型练习\n\n如下转换能否成功?如果不能,是哪一行会出错?为什么会出错?\n\n```java\npublic class Hero {\n public String name;\n protected float hp;\n \n public static void main(String[] args) {\n ADHero ad = new ADHero();\n Hero h = ad;\n AD adi = (AD) h;\n APHero ap = (APHero) adi;\n }\n}\n```\n\n\n\n| 分析 |\n| :----------------------------------------------------------- |\n| 第7行向上转型没问题 |\n| 第8行,可以将h强制转换为AD,因为 AD 与 Hero 之间通过子类关联在了一起;因此 h 指向了AD,而通过 h -> ad -> ADHero -> AD,最终也是指向AD,虽有风险,但不会报错; |\n| 第9行,不能将 adi 强制转换为 APHero,因为 AD 与 APHero之间没有关联,因此ap本应该指向 `APHero`,但通过转换 ap -> adi -> AD,即最终指向了AD,因此不能进行转换,会报错 |\n\n\n\n\n\n\n\n\"image-20210126234924701\"\n\n\n\n### instanceof 语句\n\n`instanceof Hero` 判断一个引用所指向的对象,是否是以下两种,返回 true 或 false\n\n- Hero类型\n- Hero的子类\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public static void main(String[] args) {\n ADHero ad = new ADHero();\n APHero ap = new APHero();\n \n Hero h1= ad;\n Hero h2= ap;\n \n //判断引用h1指向的对象,是否是ADHero类型\n System.out.println(h1 instanceof ADHero); \t//true\n \n //判断引用h2指向的对象,是否是APHero类型\n System.out.println(h2 instanceof APHero);\t//true\n \n //判断引用h1指向的对象,是否是Hero的子类型\n System.out.println(h1 instanceof Hero);\t\t//true\n }\n}\n```\n\n\n\n## 重写\n\n子类可以继承父类的对象方法\n\n在继承后,重复提供该方法,就叫做方法的重写\n\n又叫**覆盖** override\n\n> 父类Item有一个方法,叫做effect\n>\n> ```java\n> package property;\n> \n> public class Item {\n> String name;\n> int price;\n> \n> public void buy(){\n> System.out.println(\"购买\");\n> }\n> public void effect() {\n> System.out.println(\"物品使用后,可以有效果\");\n> }\n> \n> }\n> ```\n>\n> 子类LifePotion继承Item,同时也提供了方法effect\n>\n> ```java\n> package property;\n> \n> public class LifePotion extends Item{\n> \n> public void effect(){ \t\t\t\t\t\t//重写effect()方法\n> System.out.println(\"血瓶使用后,可以回血\");\n> }\n> \n> }\n> ```\n>\n> 调用重写的方法\n> 调用就会执行重写的方法,而不是从父类的方法\n\n重写的优点:\n\n如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。\n\n但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能**放弃继承Item**,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动.\n\n这样就增加了开发时间和维护成本\n\n\n\n## 多态\n\n### 操作符的多态\n\n同一个操作符在不同情境下,具备不同的作用:\n\n- 如果+号两侧都是整型,那么`+`代表 数字相加\n- 如果+号两侧,**任意**一个是字符串,那么`+`代表字符串连接\n\n### 类的多态\n\n多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态(实际是子类重写了父类的方法,导致子类的方法与父类的方法不同,子类之间的方法也不同)\n\n**类的多态**即父类引用指向不同的子类对象,调用同一个方法时出现不同的效果\n\n> 假设 Hero是 `ADHero` 与 `APHero` 的父类\n>\n> ```java\n> Hero h1 = new ADHero();\n> Hero h2 = new APHero();\n> ```\n\n类的多态的条件:\n\n1. 父类(接口)引用指向子类对象\n\n2. 调用的方法有[重写](#14.3 重写)\n\n### 使用类多态的好处\n\n如果不使用多态,例如:\n\n> 假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法\n> useLifePotion、useMagicPotion;\n> 除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如\n> usePurityPotion、useGuard、useInvisiblePotion等等等等\n\n```java\npackage charactor;\n \nimport property.LifePotion;\nimport property.MagicPotion;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void useLifePotion(LifePotion lp){\n lp.effect();\n }\n public void useMagicPotion(MagicPotion mp){\n mp.effect();\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n \n LifePotion lp =new LifePotion();\n MagicPotion mp =new MagicPotion();\n \n garen.useLifePotion(lp);\n garen.useMagicPotion(mp);\n \n }\n \n}\n```\n\n这个时候采用**多态**来解决这个问题:\n\n> 设计一个方法叫做useItem,其参数类型是Item\n>\n> - 如果是使用血瓶,调用该方法\n> - 如果是使用魔瓶,还是调用该方法\n> - 无论英雄要使用什么样的物品,**只需要一个方法** 即可\n\n```java\npackage charactor;\n \nimport property.Item;\nimport property.LifePotion;\nimport property.MagicPotion;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void useItem(Item i){ //设计一个方法叫做useItem,其参数类型是Item\n i.effect();\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n \n LifePotion lp =new LifePotion();\n MagicPotion mp =new MagicPotion();\n \n garen.useItem(lp); //使用血瓶lp\n garen.useItem(mp); //使用魔瓶mp\n \n }\n \n}\n```\n\n\n\n## 隐藏\n\n与重写类似:\n\n- **重写,是**子类覆盖父类的**对象方法**;\n- **隐藏**,就是子类覆盖父类的**类方法**\n\n如何隐藏呢:\n\n> 父类有一个类方法 :battleWin\n>\n> ```java\n> package charactor;\n> \n> public class Hero {\n> public String name;\n> protected float hp;\n> \n> public static void battleWin(){ //类方法,静态方法\n> System.out.println(\"hero battle win\");\n> }\n> \n> }\n> ```\n>\n> 创建一个子类,隐藏父类方法 `battleWin()`\n>\n> ```java\n> package charactor;\n> \n> public class ADHero extends Hero implements AD{\n> \n> //隐藏父类的battleWin方法\n> public static void battleWin(){\n> System.out.println(\"ad hero battle win\");\n> } \n> \n> public static void main(String[] args) {\n> Hero.battleWin(); // 调用父类的方法\n> ADHero.battleWin();//调用子类的方法\n> }\n> \n> }\n> ```\n>\n> 问:对于`Hero h =new ADHero();` \n>\n> h是父类类型的引用,但是指向一个子类对象h.battleWin(); 会调用父类的方法?还是子类的方法?\n>\n> 答:当父类的引用指向一个子类对象时,执行的:\n>\n> - 对象方法是子类的对象方法(因为重写)\n> - 类方法是父类的类方法(类方法不能被重写)\n\n## super关键字\n\n`super()`用于子类的构造函数内部;\n\n特点:\n\n1. 实例化一个父类的时候,父类的构造方法会被自动调用(根据实例化方式来选择调用有参或无参的构造方法);\n\n2. 实例化一个子类的时候,若没有写 `super()`语句,会默认调用父类无参构造方法。\n\n3. 并且,父类和子类的构造方法**都会**被调用,且父类的构造方法**先**被调用\n\n```java\npackage superKeyWord;\n//父类:\npublic class Hero{\n public Hero(){\n System.out.println(\"调用父类的构造方法\");\n }\n}\n```\n\n```java\npackage superKeyWord;\n\n//子类\npublic class SuperTest extends Hero {\n public SuperTest(){\n System.out.println(\"调用子类构造方法\");\n }\n\n public static void main(String[] args){\n new Hero();\t\t\t\t\t\t\t//创建父类对象\n System.out.println(\"---------------\");\n new SuperTest();\t\t\t\t\t//创建子类对象\n }\n \n}\n\n/*输出:\n调用父类的构造方法\n---------------\n调用父类的构造方法\n调用子类构造方法\n*/\n```\n\n### super调用父类带参构造方法\n\n`super() ` 相当于一个父类的对象,在子类中使用就类似于创建了一个父类的对象,会调用父类**带参**的构造方法\n\n`super` 与 `this` 类似,this是当前类的对象,super是父类的对象,两者都只能在方法内部使用\n\n```java\npackage superKeyWord;\n\n//父类:\npublic class Hero{\n public Hero(String name){ \t\t\t//带参的构造方法\n \n System.out.println(\"调用父类的构造方法\"+name);\n \n }\n}\n```\n\n```java\npackage superKeyWord;\n\npublic class SuperTest extends Hero {\n \n // super(\"h2\"); //super只能在方法内部使用\n \n public SuperTest(){\n \n super(\"h2\"); //正确,super在方法内使用\n \n System.out.println(\"调用子类构造方法\");\n }\n\n public static void main(String[] args){\n new Hero(\"h1\");\n System.out.println(\"---------------\");\n new SuperTest();\n }\n \n}\n\n/*输出:\n调用父类的构造方法h1\n---------------\n调用父类的构造方法h2\n调用子类构造方法\n*/\n```\n\n### super调用父类属性\n\n```java\n//父类\npackage superKeyWord;\n\npublic class Hero{\n\n int moveSpeed = 100;\n \n}\n```\n\n```java\n//子类\n\npackage superKeyWord;\n\npublic class SuperTest extends Hero {\n \n int moveSpeed = 200;\n \n public void getMoveSpeed1(){\n System.out.println(super.moveSpeed); //打印父类moveSpeed\n }\n\n public void getMoveSpeed2() {\n System.out.println(this.moveSpeed); //打印子类moveSpeed\n }\n\n public static void main(String[] args){\n \n SuperTest h = new SuperTest();\n h.getMoveSpeed1(); //输出:100\n h.getMoveSpeed2();\t\t\t\t//输出:200\n }\n \n}\n```\n\n### super调用父类方法\n\n当子类重写了父类的方法后,super调用的依然是父类原本的方法\n\n\n\n## Object类\n\nObject类是所有类的父类,即声明一个类的时候,默认就继承了Object\n\n\n\n### Object提供的一些方法\n\n- toString():\n\n 返回当前对象的**字符串表达**\n 通过 System.out.println()打印对象就是打印该对象的toString()返回值\n\n ```java\n Hero h = new Hero();\n \n //下面两行等效\n System.out.println(h);\n System.out.println(h.toString());\n \n ```\n\n- finalize():\n\n 当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件\n\n 当它被垃圾回收的时候,它的finalize() 方法就会被调用。\n\n finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用\n\n ```java\n Hero h;\n \n h = new Hero();\n h = new Hero();\n ```\n\n 执行第四行的时候,第三行的 `Hero()` 对象没了引用指向,就满足垃圾回收条件\n\n- equals():\n\n equals() 用于判断两个对象的**内容**是否相同,假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同\n\n ```java\n package charactor;\n \n public class Hero {\n public String name;\n protected float hp;\n \n public boolean equals(Object o){\n if(o instanceof Hero){\n Hero h = (Hero) o;\n return this.hp == h.hp;\n }\n return false;\n }\n \n public static void main(String[] args) {\n Hero h1= new Hero();\n h1.hp = 300;\n Hero h2= new Hero();\n h2.hp = 400;\n Hero h3= new Hero();\n h3.hp = 300;\n \n System.out.println(h1.equals(h2));\n System.out.println(h1.equals(h3));\n }\n }\n ```\n\n \n\n- `==`\n\n 这不是Object的方法,但是用于判断两个对象是否相同,\n **更准确的讲**,用于判断两个引用,是否指向了同一个对象\n\n- hashCode():\n\n 返回对象的哈希值\n\n- 线程同步方法\n\n ```java\n wait()\n notify()\n notifyAll()\n ```\n\n- getClass:\n\n 会返回一个对象的**类对象**\n\n\n\n## final修饰\n\nfinal修饰类,方法,基本类型变量,引用的时候分别有不同的意思。\n\n- 修饰类:表示该类不能被继承\n\n ```java\n public final class Hero{ //该类不能被继承\n \n }\n ```\n\n \n\n- 修饰方法:表示该方法不能被重写\n\n ```java\n public final void useItem(){\n \n } \n ```\n\n \n\n- 修饰基本变量:表示该变量只能被赋值一次\n\n ```java\n final int a;\n a = 1;\n a = 2; //报错\n ```\n\n \n\n- 修饰引用:表示该引用只有一次指向对象的机会\n\n ```java\n final Hero h;\n h = new Hero();\n h = new Hero(); //报错\n ```\n\n> 可以用 `final` 修饰变量使其作为常量使用\n>\n> 常量指的是可以公开,直接访问,不会变化的值\n\n\n\n## 抽象类\n\n### 抽象类\n\n在类中声明一个方法,这个方法没有实现体,是一个“空”方法\n\n这样的方法就叫抽象方法,使用修饰符`abstract`\n\n> 注意:\n>\n> - 当一个类有抽象方法的时候,该类必须被声明为抽象类\n>\n> - 子类继承抽象类后,必须重写抽象方法(如果有的话),赋予其具体功能\n> - 抽象类可以没有抽象方法,可以有实体方法,但是有抽象方法时必须声明为抽象类\n> - 抽象类不能直接进行实例化,即不能创建抽象类的对象\n\n```java\npackage charactor;\n \npublic abstract class Hero { //抽象类\n \n String name;\n float hp;\n float armor;\n int moveSpeed;\n \n public abstract void attack();// 抽象方法attack\n \n}\n```\n\n### 抽象类与接口区别\n\n1. 接口的方法默认是 `public`,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。\n2. 接口中除了 `static`、`final` 变量,不能有其他变量,而抽象类中则不一定。\n3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 `extends` 关键字扩展多个接口。\n4. 接口方法默认修饰符是 `public`,抽象方法可以有 `public`、`protected` 和 `default` 这些修饰符(抽象方法就是为了被重写所以不能使用 `private` 关键字修饰!)。\n5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。\n\n| 抽象类 | 接口 |\n| :------------------------------------: | :------------------: |\n| 抽象类也是类,子类只能继承一个类 | 子类可以实现多个接口 |\n| 可以是public,protected,package,private | 只能是public |\n| 静态、非静态 | 静态 |\n| final或非final的属性 | final的 |\n\n> 另外,抽象类和接口都可以有实体方法。 接口中的实体方法,叫做[默认方法](#14.11 默认方法)\n\n## 内部类\n\n内部类分为四种:\n\n- 非静态内部类\n\n 非静态内部类,只有一个外部类对象存在的时候,才有意义。也就是说,要调用非静态内部类的属性和方法,必须要先创建一个外部类\n\n ```java\n public class Hero{\n String name;\n \n class BattleScore{ //内部类\n int score;\n public void kill(){\n \n }\n }\n }\n ```\n\n 调用内部类的途径:\n\n ```java\n Hero h = new Hero(); //先创建外部类\n BattleScore bs = h.new BattleScore();\n ```\n\n \n\n- 静态内部类\n\n 在一个类里面声明一个静态内部类,静态内部类的实例化**不需要**一个外部类的实例为基础,可以直接实例化。\n\n 另外,静态内部类不能直接访问外部类的对象属性\n\n 语法:\n\n ```java\n new 外部类.静态内部类();\n ```\n\n 例:\n\n ```java\n public class Hero{\n String name;\n \n static class BattleScore{ //静态内部类\n int score;\n public void kill(){\n \n //静态内部类不能直接访问外部类的对象属性\n System.out.println(name + \"kille a Hero\");//报错\n }\n }\n }\n ```\n\n 实例化:\n\n ```java\n Hero.BattleScore bs = new Hero.BattleScore();\n ```\n\n \n\n- 匿名类\n\n 匿名类指的是在**创建某个类的对象的同时实例化它**,使代码更加简洁精练\n 通常情况下,要使用一个接口或者抽象类,都必须创建一个子类,为了快速使用,直接实例化一个抽象类,并“**当场**”实现其抽象方法。\n 既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做**匿名类**\n\n ```java\n package charactor;\n \n public abstract class Hero {\n String name; \n float hp; \n float armor; \n int moveSpeed; \n \n public abstract void attack(); //抽象方法\n \n public static void main(String[] args) {\n \n Hero h = new Hero(){\n //当场实现attack方法\n public void attack() {\n System.out.println(\"新的进攻手段\");\n }\n };\n h.attack();\n //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名\n \n System.out.println(h);\n }\n \n }\n ```\n\n 注意:在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final,但在jdk8中,已经不需要强制修饰成final了,因为编译器**偷偷的**帮你加上了看不见的final\n\n- 本地类\n\n 本地类可以理解为有名字的匿名类\n\n - **内部类**与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。\n - **本地类**和匿名类**一样**,直接声明在代码块里面,可以是主方法,for循环里等等地方\n\n ```java\n package charactor;\n \n public abstract class Hero {\n String name; \n float hp; \n float armor; \n int moveSpeed; \n \n public abstract void attack();\n \n public static void main(String[] args) {\n \n //在主方法里声明本地类\n //与匿名类的区别在于,本地类有了自定义的类名\n class SomeHero extends Hero{\n public void attack() {\n System.out.println( name+ \" 新的进攻手段\");\n }\n }\n \n SomeHero h =new SomeHero();\n h.name =\"地卜师\";\n h.attack();\n }\n \n }\n ```\n\n\n\n## 默认方法\n\n默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法\n\n例如:\n\n```java\npublic interface Mortal {\n public void die(); //抽象方法\n \n default public void revive() { //具体方法\n \n System.out.println(\"本英雄复活了\");\n }\n}\n```\n\n> 为什么会有默认方法?\n>\n> 假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的子类,都需要做改动(都要在子类中考虑重写revive的具体方法)。\n>\n> 但是引入了默认方法后,原来的类,不需要做任何改动,并且还能**得到**这个默认方法\n>\n> 通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类\n\n","source":"_posts/javaSE/14-接口与继承.md","raw":"---\ntitle: 14-接口与继承\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: ab09b476\ndate: 2021-02-14 15:38:23\ndescription: 内容较多,主要是关于类的一些知识:接口和抽象类、子类与父类的关系、内部类等\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 接口与继承\n\n## 接口\n\n### 接口\n\n接口的创建:(与创建类相似)\n\n```java\n//创建一个播放器接口\npublic interface Player{\n public void play();\n public void pause();\n public void stop();\n public void tune(); //接口只有方法声明,没有主体,并且默认都是public的,可以不写“public”\n}\n```\n\nJava接口是一系列方法的**声明**(没有主体),是一些方法特征的集合;\n\n一个接口只有方法的特征**没有方法的实现**(不能使用 new来创建实例),因此这些方法可以在不同的地方被不同的类实现(继承),进而在不同的类中实现不同的功能。\n\n接口就是一组抽象方法和常量值的集合。可以把接口看成是一种特殊的抽象类。\n\n(1)其所有的方法都必须是抽象的(abstract)。\n\n(2)其属性成员(若有)只能是final static的常量。\n\n### 实现接口\n\n实现一个接口与类的继承相似,用 `implements` 来实现\n\n```java\n// 创建一个MP3类,实现播放器功能\npublic MP3 implements Player{\n public void play(){\n System.out.println(\"播放\");\n }\n \n public void pause(){\n System.out.println(\"暂停\");\n }\n \n public void stop(){\n System.out.println(\"停止\");\n }\n \n public void tune(){\n System.out.println(\"调节音量\");\n }\n}\n```\n\n\n\n## 对象转型\n\n### 明确引用类型与对象类型的概念\n\n引用和对象都是有类型的\n\n```java\nHero h = new Hero();\n```\n\n在这个例子,引用是 `h`,对象是 `new Hero()`,它们的类型均为 `Hero`\n\n通常情况引用类型与对象类型是一样的,但也有不一样的时候\n\n### 子类转父类(向上转型)\n\n所谓的转型,是指当**引用类型**和**对象类型**不一致的时候,才需要进行类型转换;\n类型转换有时候会成功,有时候会失败\n\n假如 `ADHero` 是 `Hero` 的子类\n\n```java\nHero h = new Hero();\nADHero ad = new ADHero();\n\n```\n\n用图来表示其关系如下:\n\n\"image-20210126224549229\"\n\n```java\nh = ad; //向上转型\n```\n\n转型后的图:\n\n\"image-20210126224655825\"\n\n可以看到 `h` 经过 `ad` 最终指向的还是 `Hero`\n\n\n\n因此,把 `h` 指向 `ADHero` 一定可以,因为 `ADHero` 继承了 `Hero`,Hero能实现的功能 ADHero 也可以\n\n\n\n### 父类转子类(向下转型)\n\n父类转子类,有的时候行,**有的时候不行**,所以必须进行强制转换。\n强制转换的意思就是:转换有风险,风险自担。\n\n假如 `ADHero` 和 `Support` 是 `Hero` 的两个不同子类:\n\n```java\nHero h =new Hero();\nADHero ad = new ADHero();\nSupport s = new Support(); \n```\n\n```java\nh = s; // 向上转型,一定可以\n```\n\n向上转型一定可以,因为 `h`通过 `s` 最终还是指向 `Hero`\n\n\"image-20210126225703371\"\n\n\n\n```java\nad = (ADHero)h; //向下转型,不一定可以,需要强制转换\n```\n\n虽然 `ad` 指向的 `ADHero` 是 `Hero` 的子类,眨眼看来 `ad` 最终也是指向 Hero 的,但实际上指向的是 ADHero,ADHero与Hero还是有区别的,ADHero有的方法Hero不一定有\n\n而ad通过h最终指向的是 Hero,而不是 ADHero,因此会有风险(指向ADHero才不会有风险),只能强制转换。\n\n\"image-20210126230408061\"\n\n因为子类拥有的方法父类不一定有,因此 `h` 向下转型后,`ad`可能就没有了一些子类的方法\n\n### 没有继承关系的两个类\n\n没有继承关系的两个类,互相转换,一定会失败\n\n\n\n### 实现类转换成接口(向上转型)\n\n假如 `AD` 是一个接口,`ADHero` 类继承了它\n\n```java\npublic class Test {\n public static void main(String[] args) {\n ADHero ad = new ADHero();\n \n AD adi = ad; //向上转型 \n } \n}\n```\n\n\n\n### 转型练习\n\n如下转换能否成功?如果不能,是哪一行会出错?为什么会出错?\n\n```java\npublic class Hero {\n public String name;\n protected float hp;\n \n public static void main(String[] args) {\n ADHero ad = new ADHero();\n Hero h = ad;\n AD adi = (AD) h;\n APHero ap = (APHero) adi;\n }\n}\n```\n\n\n\n| 分析 |\n| :----------------------------------------------------------- |\n| 第7行向上转型没问题 |\n| 第8行,可以将h强制转换为AD,因为 AD 与 Hero 之间通过子类关联在了一起;因此 h 指向了AD,而通过 h -> ad -> ADHero -> AD,最终也是指向AD,虽有风险,但不会报错; |\n| 第9行,不能将 adi 强制转换为 APHero,因为 AD 与 APHero之间没有关联,因此ap本应该指向 `APHero`,但通过转换 ap -> adi -> AD,即最终指向了AD,因此不能进行转换,会报错 |\n\n\n\n\n\n\n\n\"image-20210126234924701\"\n\n\n\n### instanceof 语句\n\n`instanceof Hero` 判断一个引用所指向的对象,是否是以下两种,返回 true 或 false\n\n- Hero类型\n- Hero的子类\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public static void main(String[] args) {\n ADHero ad = new ADHero();\n APHero ap = new APHero();\n \n Hero h1= ad;\n Hero h2= ap;\n \n //判断引用h1指向的对象,是否是ADHero类型\n System.out.println(h1 instanceof ADHero); \t//true\n \n //判断引用h2指向的对象,是否是APHero类型\n System.out.println(h2 instanceof APHero);\t//true\n \n //判断引用h1指向的对象,是否是Hero的子类型\n System.out.println(h1 instanceof Hero);\t\t//true\n }\n}\n```\n\n\n\n## 重写\n\n子类可以继承父类的对象方法\n\n在继承后,重复提供该方法,就叫做方法的重写\n\n又叫**覆盖** override\n\n> 父类Item有一个方法,叫做effect\n>\n> ```java\n> package property;\n> \n> public class Item {\n> String name;\n> int price;\n> \n> public void buy(){\n> System.out.println(\"购买\");\n> }\n> public void effect() {\n> System.out.println(\"物品使用后,可以有效果\");\n> }\n> \n> }\n> ```\n>\n> 子类LifePotion继承Item,同时也提供了方法effect\n>\n> ```java\n> package property;\n> \n> public class LifePotion extends Item{\n> \n> public void effect(){ \t\t\t\t\t\t//重写effect()方法\n> System.out.println(\"血瓶使用后,可以回血\");\n> }\n> \n> }\n> ```\n>\n> 调用重写的方法\n> 调用就会执行重写的方法,而不是从父类的方法\n\n重写的优点:\n\n如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。\n\n但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能**放弃继承Item**,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动.\n\n这样就增加了开发时间和维护成本\n\n\n\n## 多态\n\n### 操作符的多态\n\n同一个操作符在不同情境下,具备不同的作用:\n\n- 如果+号两侧都是整型,那么`+`代表 数字相加\n- 如果+号两侧,**任意**一个是字符串,那么`+`代表字符串连接\n\n### 类的多态\n\n多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态(实际是子类重写了父类的方法,导致子类的方法与父类的方法不同,子类之间的方法也不同)\n\n**类的多态**即父类引用指向不同的子类对象,调用同一个方法时出现不同的效果\n\n> 假设 Hero是 `ADHero` 与 `APHero` 的父类\n>\n> ```java\n> Hero h1 = new ADHero();\n> Hero h2 = new APHero();\n> ```\n\n类的多态的条件:\n\n1. 父类(接口)引用指向子类对象\n\n2. 调用的方法有[重写](#14.3 重写)\n\n### 使用类多态的好处\n\n如果不使用多态,例如:\n\n> 假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法\n> useLifePotion、useMagicPotion;\n> 除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如\n> usePurityPotion、useGuard、useInvisiblePotion等等等等\n\n```java\npackage charactor;\n \nimport property.LifePotion;\nimport property.MagicPotion;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void useLifePotion(LifePotion lp){\n lp.effect();\n }\n public void useMagicPotion(MagicPotion mp){\n mp.effect();\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n \n LifePotion lp =new LifePotion();\n MagicPotion mp =new MagicPotion();\n \n garen.useLifePotion(lp);\n garen.useMagicPotion(mp);\n \n }\n \n}\n```\n\n这个时候采用**多态**来解决这个问题:\n\n> 设计一个方法叫做useItem,其参数类型是Item\n>\n> - 如果是使用血瓶,调用该方法\n> - 如果是使用魔瓶,还是调用该方法\n> - 无论英雄要使用什么样的物品,**只需要一个方法** 即可\n\n```java\npackage charactor;\n \nimport property.Item;\nimport property.LifePotion;\nimport property.MagicPotion;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void useItem(Item i){ //设计一个方法叫做useItem,其参数类型是Item\n i.effect();\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n \n LifePotion lp =new LifePotion();\n MagicPotion mp =new MagicPotion();\n \n garen.useItem(lp); //使用血瓶lp\n garen.useItem(mp); //使用魔瓶mp\n \n }\n \n}\n```\n\n\n\n## 隐藏\n\n与重写类似:\n\n- **重写,是**子类覆盖父类的**对象方法**;\n- **隐藏**,就是子类覆盖父类的**类方法**\n\n如何隐藏呢:\n\n> 父类有一个类方法 :battleWin\n>\n> ```java\n> package charactor;\n> \n> public class Hero {\n> public String name;\n> protected float hp;\n> \n> public static void battleWin(){ //类方法,静态方法\n> System.out.println(\"hero battle win\");\n> }\n> \n> }\n> ```\n>\n> 创建一个子类,隐藏父类方法 `battleWin()`\n>\n> ```java\n> package charactor;\n> \n> public class ADHero extends Hero implements AD{\n> \n> //隐藏父类的battleWin方法\n> public static void battleWin(){\n> System.out.println(\"ad hero battle win\");\n> } \n> \n> public static void main(String[] args) {\n> Hero.battleWin(); // 调用父类的方法\n> ADHero.battleWin();//调用子类的方法\n> }\n> \n> }\n> ```\n>\n> 问:对于`Hero h =new ADHero();` \n>\n> h是父类类型的引用,但是指向一个子类对象h.battleWin(); 会调用父类的方法?还是子类的方法?\n>\n> 答:当父类的引用指向一个子类对象时,执行的:\n>\n> - 对象方法是子类的对象方法(因为重写)\n> - 类方法是父类的类方法(类方法不能被重写)\n\n## super关键字\n\n`super()`用于子类的构造函数内部;\n\n特点:\n\n1. 实例化一个父类的时候,父类的构造方法会被自动调用(根据实例化方式来选择调用有参或无参的构造方法);\n\n2. 实例化一个子类的时候,若没有写 `super()`语句,会默认调用父类无参构造方法。\n\n3. 并且,父类和子类的构造方法**都会**被调用,且父类的构造方法**先**被调用\n\n```java\npackage superKeyWord;\n//父类:\npublic class Hero{\n public Hero(){\n System.out.println(\"调用父类的构造方法\");\n }\n}\n```\n\n```java\npackage superKeyWord;\n\n//子类\npublic class SuperTest extends Hero {\n public SuperTest(){\n System.out.println(\"调用子类构造方法\");\n }\n\n public static void main(String[] args){\n new Hero();\t\t\t\t\t\t\t//创建父类对象\n System.out.println(\"---------------\");\n new SuperTest();\t\t\t\t\t//创建子类对象\n }\n \n}\n\n/*输出:\n调用父类的构造方法\n---------------\n调用父类的构造方法\n调用子类构造方法\n*/\n```\n\n### super调用父类带参构造方法\n\n`super() ` 相当于一个父类的对象,在子类中使用就类似于创建了一个父类的对象,会调用父类**带参**的构造方法\n\n`super` 与 `this` 类似,this是当前类的对象,super是父类的对象,两者都只能在方法内部使用\n\n```java\npackage superKeyWord;\n\n//父类:\npublic class Hero{\n public Hero(String name){ \t\t\t//带参的构造方法\n \n System.out.println(\"调用父类的构造方法\"+name);\n \n }\n}\n```\n\n```java\npackage superKeyWord;\n\npublic class SuperTest extends Hero {\n \n // super(\"h2\"); //super只能在方法内部使用\n \n public SuperTest(){\n \n super(\"h2\"); //正确,super在方法内使用\n \n System.out.println(\"调用子类构造方法\");\n }\n\n public static void main(String[] args){\n new Hero(\"h1\");\n System.out.println(\"---------------\");\n new SuperTest();\n }\n \n}\n\n/*输出:\n调用父类的构造方法h1\n---------------\n调用父类的构造方法h2\n调用子类构造方法\n*/\n```\n\n### super调用父类属性\n\n```java\n//父类\npackage superKeyWord;\n\npublic class Hero{\n\n int moveSpeed = 100;\n \n}\n```\n\n```java\n//子类\n\npackage superKeyWord;\n\npublic class SuperTest extends Hero {\n \n int moveSpeed = 200;\n \n public void getMoveSpeed1(){\n System.out.println(super.moveSpeed); //打印父类moveSpeed\n }\n\n public void getMoveSpeed2() {\n System.out.println(this.moveSpeed); //打印子类moveSpeed\n }\n\n public static void main(String[] args){\n \n SuperTest h = new SuperTest();\n h.getMoveSpeed1(); //输出:100\n h.getMoveSpeed2();\t\t\t\t//输出:200\n }\n \n}\n```\n\n### super调用父类方法\n\n当子类重写了父类的方法后,super调用的依然是父类原本的方法\n\n\n\n## Object类\n\nObject类是所有类的父类,即声明一个类的时候,默认就继承了Object\n\n\n\n### Object提供的一些方法\n\n- toString():\n\n 返回当前对象的**字符串表达**\n 通过 System.out.println()打印对象就是打印该对象的toString()返回值\n\n ```java\n Hero h = new Hero();\n \n //下面两行等效\n System.out.println(h);\n System.out.println(h.toString());\n \n ```\n\n- finalize():\n\n 当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件\n\n 当它被垃圾回收的时候,它的finalize() 方法就会被调用。\n\n finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用\n\n ```java\n Hero h;\n \n h = new Hero();\n h = new Hero();\n ```\n\n 执行第四行的时候,第三行的 `Hero()` 对象没了引用指向,就满足垃圾回收条件\n\n- equals():\n\n equals() 用于判断两个对象的**内容**是否相同,假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同\n\n ```java\n package charactor;\n \n public class Hero {\n public String name;\n protected float hp;\n \n public boolean equals(Object o){\n if(o instanceof Hero){\n Hero h = (Hero) o;\n return this.hp == h.hp;\n }\n return false;\n }\n \n public static void main(String[] args) {\n Hero h1= new Hero();\n h1.hp = 300;\n Hero h2= new Hero();\n h2.hp = 400;\n Hero h3= new Hero();\n h3.hp = 300;\n \n System.out.println(h1.equals(h2));\n System.out.println(h1.equals(h3));\n }\n }\n ```\n\n \n\n- `==`\n\n 这不是Object的方法,但是用于判断两个对象是否相同,\n **更准确的讲**,用于判断两个引用,是否指向了同一个对象\n\n- hashCode():\n\n 返回对象的哈希值\n\n- 线程同步方法\n\n ```java\n wait()\n notify()\n notifyAll()\n ```\n\n- getClass:\n\n 会返回一个对象的**类对象**\n\n\n\n## final修饰\n\nfinal修饰类,方法,基本类型变量,引用的时候分别有不同的意思。\n\n- 修饰类:表示该类不能被继承\n\n ```java\n public final class Hero{ //该类不能被继承\n \n }\n ```\n\n \n\n- 修饰方法:表示该方法不能被重写\n\n ```java\n public final void useItem(){\n \n } \n ```\n\n \n\n- 修饰基本变量:表示该变量只能被赋值一次\n\n ```java\n final int a;\n a = 1;\n a = 2; //报错\n ```\n\n \n\n- 修饰引用:表示该引用只有一次指向对象的机会\n\n ```java\n final Hero h;\n h = new Hero();\n h = new Hero(); //报错\n ```\n\n> 可以用 `final` 修饰变量使其作为常量使用\n>\n> 常量指的是可以公开,直接访问,不会变化的值\n\n\n\n## 抽象类\n\n### 抽象类\n\n在类中声明一个方法,这个方法没有实现体,是一个“空”方法\n\n这样的方法就叫抽象方法,使用修饰符`abstract`\n\n> 注意:\n>\n> - 当一个类有抽象方法的时候,该类必须被声明为抽象类\n>\n> - 子类继承抽象类后,必须重写抽象方法(如果有的话),赋予其具体功能\n> - 抽象类可以没有抽象方法,可以有实体方法,但是有抽象方法时必须声明为抽象类\n> - 抽象类不能直接进行实例化,即不能创建抽象类的对象\n\n```java\npackage charactor;\n \npublic abstract class Hero { //抽象类\n \n String name;\n float hp;\n float armor;\n int moveSpeed;\n \n public abstract void attack();// 抽象方法attack\n \n}\n```\n\n### 抽象类与接口区别\n\n1. 接口的方法默认是 `public`,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。\n2. 接口中除了 `static`、`final` 变量,不能有其他变量,而抽象类中则不一定。\n3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 `extends` 关键字扩展多个接口。\n4. 接口方法默认修饰符是 `public`,抽象方法可以有 `public`、`protected` 和 `default` 这些修饰符(抽象方法就是为了被重写所以不能使用 `private` 关键字修饰!)。\n5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。\n\n| 抽象类 | 接口 |\n| :------------------------------------: | :------------------: |\n| 抽象类也是类,子类只能继承一个类 | 子类可以实现多个接口 |\n| 可以是public,protected,package,private | 只能是public |\n| 静态、非静态 | 静态 |\n| final或非final的属性 | final的 |\n\n> 另外,抽象类和接口都可以有实体方法。 接口中的实体方法,叫做[默认方法](#14.11 默认方法)\n\n## 内部类\n\n内部类分为四种:\n\n- 非静态内部类\n\n 非静态内部类,只有一个外部类对象存在的时候,才有意义。也就是说,要调用非静态内部类的属性和方法,必须要先创建一个外部类\n\n ```java\n public class Hero{\n String name;\n \n class BattleScore{ //内部类\n int score;\n public void kill(){\n \n }\n }\n }\n ```\n\n 调用内部类的途径:\n\n ```java\n Hero h = new Hero(); //先创建外部类\n BattleScore bs = h.new BattleScore();\n ```\n\n \n\n- 静态内部类\n\n 在一个类里面声明一个静态内部类,静态内部类的实例化**不需要**一个外部类的实例为基础,可以直接实例化。\n\n 另外,静态内部类不能直接访问外部类的对象属性\n\n 语法:\n\n ```java\n new 外部类.静态内部类();\n ```\n\n 例:\n\n ```java\n public class Hero{\n String name;\n \n static class BattleScore{ //静态内部类\n int score;\n public void kill(){\n \n //静态内部类不能直接访问外部类的对象属性\n System.out.println(name + \"kille a Hero\");//报错\n }\n }\n }\n ```\n\n 实例化:\n\n ```java\n Hero.BattleScore bs = new Hero.BattleScore();\n ```\n\n \n\n- 匿名类\n\n 匿名类指的是在**创建某个类的对象的同时实例化它**,使代码更加简洁精练\n 通常情况下,要使用一个接口或者抽象类,都必须创建一个子类,为了快速使用,直接实例化一个抽象类,并“**当场**”实现其抽象方法。\n 既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做**匿名类**\n\n ```java\n package charactor;\n \n public abstract class Hero {\n String name; \n float hp; \n float armor; \n int moveSpeed; \n \n public abstract void attack(); //抽象方法\n \n public static void main(String[] args) {\n \n Hero h = new Hero(){\n //当场实现attack方法\n public void attack() {\n System.out.println(\"新的进攻手段\");\n }\n };\n h.attack();\n //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名\n \n System.out.println(h);\n }\n \n }\n ```\n\n 注意:在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final,但在jdk8中,已经不需要强制修饰成final了,因为编译器**偷偷的**帮你加上了看不见的final\n\n- 本地类\n\n 本地类可以理解为有名字的匿名类\n\n - **内部类**与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。\n - **本地类**和匿名类**一样**,直接声明在代码块里面,可以是主方法,for循环里等等地方\n\n ```java\n package charactor;\n \n public abstract class Hero {\n String name; \n float hp; \n float armor; \n int moveSpeed; \n \n public abstract void attack();\n \n public static void main(String[] args) {\n \n //在主方法里声明本地类\n //与匿名类的区别在于,本地类有了自定义的类名\n class SomeHero extends Hero{\n public void attack() {\n System.out.println( name+ \" 新的进攻手段\");\n }\n }\n \n SomeHero h =new SomeHero();\n h.name =\"地卜师\";\n h.attack();\n }\n \n }\n ```\n\n\n\n## 默认方法\n\n默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法\n\n例如:\n\n```java\npublic interface Mortal {\n public void die(); //抽象方法\n \n default public void revive() { //具体方法\n \n System.out.println(\"本英雄复活了\");\n }\n}\n```\n\n> 为什么会有默认方法?\n>\n> 假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的子类,都需要做改动(都要在子类中考虑重写revive的具体方法)。\n>\n> 但是引入了默认方法后,原来的类,不需要做任何改动,并且还能**得到**这个默认方法\n>\n> 通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类\n\n","slug":"javaSE/14-接口与继承","published":1,"updated":"2021-08-29T15:31:35.517Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6tw00h8akveg72k4lri","content":"

接口与继承

接口

接口

接口的创建:(与创建类相似)

\n
1
2
3
4
5
6
7
//创建一个播放器接口
public interface Player{
public void play();
public void pause();
public void stop();
public void tune(); //接口只有方法声明,没有主体,并且默认都是public的,可以不写“public”
}
\n

Java接口是一系列方法的声明(没有主体),是一些方法特征的集合;

\n

一个接口只有方法的特征没有方法的实现(不能使用 new来创建实例),因此这些方法可以在不同的地方被不同的类实现(继承),进而在不同的类中实现不同的功能。

\n

接口就是一组抽象方法和常量值的集合。可以把接口看成是一种特殊的抽象类。

\n

(1)其所有的方法都必须是抽象的(abstract)。

\n

(2)其属性成员(若有)只能是final static的常量。

\n

实现接口

实现一个接口与类的继承相似,用 implements 来实现

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建一个MP3类,实现播放器功能
public MP3 implements Player{
public void play(){
System.out.println("播放");
}

public void pause(){
System.out.println("暂停");
}

public void stop(){
System.out.println("停止");
}

public void tune(){
System.out.println("调节音量");
}
}
\n

对象转型

明确引用类型与对象类型的概念

引用和对象都是有类型的

\n
1
Hero h = new Hero();
\n

在这个例子,引用是 h,对象是 new Hero(),它们的类型均为 Hero

\n

通常情况引用类型与对象类型是一样的,但也有不一样的时候

\n

子类转父类(向上转型)

所谓的转型,是指当引用类型对象类型不一致的时候,才需要进行类型转换;
类型转换有时候会成功,有时候会失败

\n

假如 ADHeroHero 的子类

\n
1
2
3
Hero h = new Hero();
ADHero ad = new ADHero();

\n

用图来表示其关系如下:

\n

\"image-20210126224549229\"

\n
1
h = ad;                      //向上转型
\n

转型后的图:

\n

\"image-20210126224655825\"

\n

可以看到 h 经过 ad 最终指向的还是 Hero

\n

因此,把 h 指向 ADHero 一定可以,因为 ADHero 继承了 Hero,Hero能实现的功能 ADHero 也可以

\n

父类转子类(向下转型)

父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。
强制转换的意思就是:转换有风险,风险自担。

\n

假如 ADHeroSupportHero 的两个不同子类:

\n
1
2
3
Hero h =new Hero();
ADHero ad = new ADHero();
Support s = new Support();
\n
1
h = s;            // 向上转型,一定可以
\n

向上转型一定可以,因为 h通过 s 最终还是指向 Hero

\n

\"image-20210126225703371\"

\n
1
ad = (ADHero)h;    //向下转型,不一定可以,需要强制转换
\n

虽然 ad 指向的 ADHeroHero 的子类,眨眼看来 ad 最终也是指向 Hero 的,但实际上指向的是 ADHero,ADHero与Hero还是有区别的,ADHero有的方法Hero不一定有

\n

而ad通过h最终指向的是 Hero,而不是 ADHero,因此会有风险(指向ADHero才不会有风险),只能强制转换。

\n

\"image-20210126230408061\"

\n

因为子类拥有的方法父类不一定有,因此 h 向下转型后,ad可能就没有了一些子类的方法

\n

没有继承关系的两个类

没有继承关系的两个类,互相转换,一定会失败

\n

实现类转换成接口(向上转型)

假如 AD 是一个接口,ADHero 类继承了它

\n
1
2
3
4
5
6
7
public class Test {
public static void main(String[] args) {
ADHero ad = new ADHero();

AD adi = ad; //向上转型
}
}
\n

转型练习

如下转换能否成功?如果不能,是哪一行会出错?为什么会出错?

\n
1
2
3
4
5
6
7
8
9
10
11
public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {
ADHero ad = new ADHero();
Hero h = ad;
AD adi = (AD) h;
APHero ap = (APHero) adi;
}
}
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
分析
第7行向上转型没问题
第8行,可以将h强制转换为AD,因为 AD 与 Hero 之间通过子类关联在了一起;因此 h 指向了AD,而通过 h -> ad -> ADHero -> AD,最终也是指向AD,虽有风险,但不会报错;
第9行,不能将 adi 强制转换为 APHero,因为 AD 与 APHero之间没有关联,因此ap本应该指向 APHero,但通过转换 ap -> adi -> AD,即最终指向了AD,因此不能进行转换,会报错
\n
\n

\"image-20210126234924701\"

\n

instanceof 语句

instanceof Hero 判断一个引用所指向的对象,是否是以下两种,返回 true 或 false

\n
    \n
  • Hero类型
  • \n
  • Hero的子类
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package charactor;

public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {
ADHero ad = new ADHero();
APHero ap = new APHero();

Hero h1= ad;
Hero h2= ap;

//判断引用h1指向的对象,是否是ADHero类型
System.out.println(h1 instanceof ADHero); \t//true

//判断引用h2指向的对象,是否是APHero类型
System.out.println(h2 instanceof APHero);\t//true

//判断引用h1指向的对象,是否是Hero的子类型
System.out.println(h1 instanceof Hero);\t\t//true
}
}
\n

重写

子类可以继承父类的对象方法

\n

在继承后,重复提供该方法,就叫做方法的重写

\n

又叫覆盖 override

\n
\n

父类Item有一个方法,叫做effect

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package property;

public class Item {
String name;
int price;

public void buy(){
System.out.println("购买");
}
public void effect() {
System.out.println("物品使用后,可以有效果");
}

}
\n

子类LifePotion继承Item,同时也提供了方法effect

\n
1
2
3
4
5
6
7
8
9
package property;

public class LifePotion extends Item{

public void effect(){ \t\t\t\t\t\t//重写effect()方法
System.out.println("血瓶使用后,可以回血");
}

}
\n

调用重写的方法
调用就会执行重写的方法,而不是从父类的方法

\n
\n

重写的优点:

\n

如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。

\n

但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能放弃继承Item,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动.

\n

这样就增加了开发时间和维护成本

\n

多态

操作符的多态

同一个操作符在不同情境下,具备不同的作用:

\n
    \n
  • 如果+号两侧都是整型,那么+代表 数字相加
  • \n
  • 如果+号两侧,任意一个是字符串,那么+代表字符串连接
  • \n
\n

类的多态

多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态(实际是子类重写了父类的方法,导致子类的方法与父类的方法不同,子类之间的方法也不同)

\n

类的多态即父类引用指向不同的子类对象,调用同一个方法时出现不同的效果

\n
\n

假设 Hero是 ADHeroAPHero 的父类

\n
1
2
Hero h1 = new ADHero();
Hero h2 = new APHero();
\n
\n

类的多态的条件:

\n
    \n
  1. 父类(接口)引用指向子类对象

    \n
  2. \n
  3. 调用的方法有重写

    \n
  4. \n
\n

使用类多态的好处

如果不使用多态,例如:

\n
\n

假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法
useLifePotion、useMagicPotion;
除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如
usePurityPotion、useGuard、useInvisiblePotion等等等等

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package charactor;

import property.LifePotion;
import property.MagicPotion;

public class Hero {
public String name;
protected float hp;

public void useLifePotion(LifePotion lp){
lp.effect();
}
public void useMagicPotion(MagicPotion mp){
mp.effect();
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";

LifePotion lp =new LifePotion();
MagicPotion mp =new MagicPotion();

garen.useLifePotion(lp);
garen.useMagicPotion(mp);

}

}
\n

这个时候采用多态来解决这个问题:

\n
\n

设计一个方法叫做useItem,其参数类型是Item

\n
    \n
  • 如果是使用血瓶,调用该方法
  • \n
  • 如果是使用魔瓶,还是调用该方法
  • \n
  • 无论英雄要使用什么样的物品,只需要一个方法 即可
  • \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package charactor;

import property.Item;
import property.LifePotion;
import property.MagicPotion;

public class Hero {
public String name;
protected float hp;

public void useItem(Item i){ //设计一个方法叫做useItem,其参数类型是Item
i.effect();
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";

LifePotion lp =new LifePotion();
MagicPotion mp =new MagicPotion();

garen.useItem(lp); //使用血瓶lp
garen.useItem(mp); //使用魔瓶mp

}

}
\n

隐藏

与重写类似:

\n
    \n
  • 重写,是子类覆盖父类的对象方法
  • \n
  • 隐藏,就是子类覆盖父类的类方法
  • \n
\n

如何隐藏呢:

\n
\n

父类有一个类方法 :battleWin

\n
1
2
3
4
5
6
7
8
9
10
11
package charactor;

public class Hero {
public String name;
protected float hp;

public static void battleWin(){ //类方法,静态方法
System.out.println("hero battle win");
}

}
\n

创建一个子类,隐藏父类方法 battleWin()

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package charactor;

public class ADHero extends Hero implements AD{

//隐藏父类的battleWin方法
public static void battleWin(){
System.out.println("ad hero battle win");
}

public static void main(String[] args) {
Hero.battleWin(); // 调用父类的方法
ADHero.battleWin();//调用子类的方法
}

}
\n

问:对于Hero h =new ADHero();

\n

h是父类类型的引用,但是指向一个子类对象h.battleWin(); 会调用父类的方法?还是子类的方法?

\n

答:当父类的引用指向一个子类对象时,执行的:

\n
    \n
  • 对象方法是子类的对象方法(因为重写)
  • \n
  • 类方法是父类的类方法(类方法不能被重写)
  • \n
\n
\n

super关键字

super()用于子类的构造函数内部;

\n

特点:

\n
    \n
  1. 实例化一个父类的时候,父类的构造方法会被自动调用(根据实例化方式来选择调用有参或无参的构造方法);

    \n
  2. \n
  3. 实例化一个子类的时候,若没有写 super()语句,会默认调用父类无参构造方法。

    \n
  4. \n
  5. 并且,父类和子类的构造方法都会被调用,且父类的构造方法被调用

    \n
  6. \n
\n
1
2
3
4
5
6
7
package superKeyWord;
//父类:
public class Hero{
public Hero(){
System.out.println("调用父类的构造方法");
}
}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package superKeyWord;

//子类
public class SuperTest extends Hero {
public SuperTest(){
System.out.println("调用子类构造方法");
}

public static void main(String[] args){
new Hero();\t\t\t\t\t\t\t//创建父类对象
System.out.println("---------------");
new SuperTest();\t\t\t\t\t//创建子类对象
}

}

/*输出:
调用父类的构造方法
---------------
调用父类的构造方法
调用子类构造方法
*/
\n

super调用父类带参构造方法

super() 相当于一个父类的对象,在子类中使用就类似于创建了一个父类的对象,会调用父类带参的构造方法

\n

superthis 类似,this是当前类的对象,super是父类的对象,两者都只能在方法内部使用

\n
1
2
3
4
5
6
7
8
9
10
package superKeyWord;

//父类:
public class Hero{
public Hero(String name){ \t\t\t//带参的构造方法

System.out.println("调用父类的构造方法"+name);

}
}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package superKeyWord;

public class SuperTest extends Hero {

// super("h2"); //super只能在方法内部使用

public SuperTest(){

super("h2"); //正确,super在方法内使用

System.out.println("调用子类构造方法");
}

public static void main(String[] args){
new Hero("h1");
System.out.println("---------------");
new SuperTest();
}

}

/*输出:
调用父类的构造方法h1
---------------
调用父类的构造方法h2
调用子类构造方法
*/
\n

super调用父类属性

1
2
3
4
5
6
7
8
//父类
package superKeyWord;

public class Hero{

int moveSpeed = 100;

}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//子类

package superKeyWord;

public class SuperTest extends Hero {

int moveSpeed = 200;

public void getMoveSpeed1(){
System.out.println(super.moveSpeed); //打印父类moveSpeed
}

public void getMoveSpeed2() {
System.out.println(this.moveSpeed); //打印子类moveSpeed
}

public static void main(String[] args){

SuperTest h = new SuperTest();
h.getMoveSpeed1(); //输出:100
h.getMoveSpeed2();\t\t\t\t//输出:200
}

}
\n

super调用父类方法

当子类重写了父类的方法后,super调用的依然是父类原本的方法

\n

Object类

Object类是所有类的父类,即声明一个类的时候,默认就继承了Object

\n

Object提供的一些方法

    \n
  • toString():

    \n

    返回当前对象的字符串表达
    通过 System.out.println()打印对象就是打印该对象的toString()返回值

    \n
    1
    2
    3
    4
    5
    6
    Hero h = new Hero();

    //下面两行等效
    System.out.println(h);
    System.out.println(h.toString());

    \n
  • \n
  • finalize():

    \n

    当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件

    \n

    当它被垃圾回收的时候,它的finalize() 方法就会被调用。

    \n

    finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用

    \n
    1
    2
    3
    4
    Hero h;

    h = new Hero();
    h = new Hero();
    \n

    执行第四行的时候,第三行的 Hero() 对象没了引用指向,就满足垃圾回收条件

    \n
  • \n
  • equals():

    \n

    equals() 用于判断两个对象的内容是否相同,假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package charactor;

    public class Hero {
    public String name;
    protected float hp;

    public boolean equals(Object o){
    if(o instanceof Hero){
    Hero h = (Hero) o;
    return this.hp == h.hp;
    }
    return false;
    }

    public static void main(String[] args) {
    Hero h1= new Hero();
    h1.hp = 300;
    Hero h2= new Hero();
    h2.hp = 400;
    Hero h3= new Hero();
    h3.hp = 300;

    System.out.println(h1.equals(h2));
    System.out.println(h1.equals(h3));
    }
    }
    \n
  • \n
\n
    \n
  • ==

    \n

    这不是Object的方法,但是用于判断两个对象是否相同,
    更准确的讲,用于判断两个引用,是否指向了同一个对象

    \n
  • \n
  • hashCode():

    \n

    返回对象的哈希值

    \n
  • \n
  • 线程同步方法

    \n
    1
    2
    3
    wait()
    notify()
    notifyAll()
    \n
  • \n
  • getClass:

    \n

    会返回一个对象的类对象

    \n
  • \n
\n

final修饰

final修饰类,方法,基本类型变量,引用的时候分别有不同的意思。

\n
    \n
  • 修饰类:表示该类不能被继承

    \n
    1
    2
    3
    public final class Hero{   //该类不能被继承

    }
    \n
  • \n
\n
    \n
  • 修饰方法:表示该方法不能被重写

    \n
    1
    2
    3
    public final void useItem(){

    }
    \n
  • \n
\n
    \n
  • 修饰基本变量:表示该变量只能被赋值一次

    \n
    1
    2
    3
    final int a;
    a = 1;
    a = 2; //报错
    \n
  • \n
\n
    \n
  • 修饰引用:表示该引用只有一次指向对象的机会

    \n
    1
    2
    3
    final Hero h;
    h = new Hero();
    h = new Hero(); //报错
    \n
  • \n
\n
\n

可以用 final 修饰变量使其作为常量使用

\n

常量指的是可以公开,直接访问,不会变化的值

\n
\n

抽象类

抽象类

在类中声明一个方法,这个方法没有实现体,是一个“空”方法

\n

这样的方法就叫抽象方法,使用修饰符abstract

\n
\n

注意:

\n
    \n
  • 当一个类有抽象方法的时候,该类必须被声明为抽象类

    \n
  • \n
  • 子类继承抽象类后,必须重写抽象方法(如果有的话),赋予其具体功能

    \n
  • \n
  • 抽象类可以没有抽象方法,可以有实体方法,但是有抽象方法时必须声明为抽象类
  • \n
  • 抽象类不能直接进行实例化,即不能创建抽象类的对象
  • \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
package charactor;

public abstract class Hero { //抽象类

String name;
float hp;
float armor;
int moveSpeed;

public abstract void attack();// 抽象方法attack

}
\n

抽象类与接口区别

    \n
  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  2. \n
  3. 接口中除了 staticfinal 变量,不能有其他变量,而抽象类中则不一定。
  4. \n
  5. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
  6. \n
  7. 接口方法默认修饰符是 public,抽象方法可以有 publicprotecteddefault 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!)。
  8. \n
  9. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
  10. \n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
抽象类接口
抽象类也是类,子类只能继承一个类子类可以实现多个接口
可以是public,protected,package,private只能是public
静态、非静态静态
final或非final的属性final的
\n
\n
\n

另外,抽象类和接口都可以有实体方法。 接口中的实体方法,叫做默认方法

\n
\n

内部类

内部类分为四种:

\n
    \n
  • 非静态内部类

    \n

    非静态内部类,只有一个外部类对象存在的时候,才有意义。也就是说,要调用非静态内部类的属性和方法,必须要先创建一个外部类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Hero{
    String name;

    class BattleScore{ //内部类
    int score;
    public void kill(){

    }
    }
    }
    \n

    调用内部类的途径:

    \n
    1
    2
    Hero h = new Hero();  //先创建外部类
    BattleScore bs = h.new BattleScore();
    \n
  • \n
\n
    \n
  • 静态内部类

    \n

    在一个类里面声明一个静态内部类,静态内部类的实例化不需要一个外部类的实例为基础,可以直接实例化。

    \n

    另外,静态内部类不能直接访问外部类的对象属性

    \n

    语法:

    \n
    1
    new 外部类.静态内部类();
    \n

    例:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Hero{
    String name;

    static class BattleScore{ //静态内部类
    int score;
    public void kill(){

    //静态内部类不能直接访问外部类的对象属性
    System.out.println(name + "kille a Hero");//报错
    }
    }
    }
    \n

    实例化:

    \n
    1
    Hero.BattleScore bs = new Hero.BattleScore();
    \n
  • \n
\n
    \n
  • 匿名类

    \n

    匿名类指的是在创建某个类的对象的同时实例化它,使代码更加简洁精练
    通常情况下,要使用一个接口或者抽象类,都必须创建一个子类,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
    既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做匿名类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package charactor;

    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack(); //抽象方法

    public static void main(String[] args) {

    Hero h = new Hero(){
    //当场实现attack方法
    public void attack() {
    System.out.println("新的进攻手段");
    }
    };
    h.attack();
    //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名

    System.out.println(h);
    }

    }
    \n

    注意:在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final,但在jdk8中,已经不需要强制修饰成final了,因为编译器偷偷的帮你加上了看不见的final

    \n
  • \n
  • 本地类

    \n

    本地类可以理解为有名字的匿名类

    \n
      \n
    • 内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。
    • \n
    • 本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package charactor;

    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack();

    public static void main(String[] args) {

    //在主方法里声明本地类
    //与匿名类的区别在于,本地类有了自定义的类名
    class SomeHero extends Hero{
    public void attack() {
    System.out.println( name+ " 新的进攻手段");
    }
    }

    SomeHero h =new SomeHero();
    h.name ="地卜师";
    h.attack();
    }

    }
    \n
  • \n
\n

默认方法

默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法

\n

例如:

\n
1
2
3
4
5
6
7
8
public interface Mortal {
public void die(); //抽象方法

default public void revive() { //具体方法

System.out.println("本英雄复活了");
}
}
\n
\n

为什么会有默认方法?

\n

假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的子类,都需要做改动(都要在子类中考虑重写revive的具体方法)。

\n

但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法

\n

通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

接口与继承

接口

接口

接口的创建:(与创建类相似)

\n
1
2
3
4
5
6
7
//创建一个播放器接口
public interface Player{
public void play();
public void pause();
public void stop();
public void tune(); //接口只有方法声明,没有主体,并且默认都是public的,可以不写“public”
}
\n

Java接口是一系列方法的声明(没有主体),是一些方法特征的集合;

\n

一个接口只有方法的特征没有方法的实现(不能使用 new来创建实例),因此这些方法可以在不同的地方被不同的类实现(继承),进而在不同的类中实现不同的功能。

\n

接口就是一组抽象方法和常量值的集合。可以把接口看成是一种特殊的抽象类。

\n

(1)其所有的方法都必须是抽象的(abstract)。

\n

(2)其属性成员(若有)只能是final static的常量。

\n

实现接口

实现一个接口与类的继承相似,用 implements 来实现

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建一个MP3类,实现播放器功能
public MP3 implements Player{
public void play(){
System.out.println("播放");
}

public void pause(){
System.out.println("暂停");
}

public void stop(){
System.out.println("停止");
}

public void tune(){
System.out.println("调节音量");
}
}
\n

对象转型

明确引用类型与对象类型的概念

引用和对象都是有类型的

\n
1
Hero h = new Hero();
\n

在这个例子,引用是 h,对象是 new Hero(),它们的类型均为 Hero

\n

通常情况引用类型与对象类型是一样的,但也有不一样的时候

\n

子类转父类(向上转型)

所谓的转型,是指当引用类型对象类型不一致的时候,才需要进行类型转换;
类型转换有时候会成功,有时候会失败

\n

假如 ADHeroHero 的子类

\n
1
2
3
Hero h = new Hero();
ADHero ad = new ADHero();

\n

用图来表示其关系如下:

\n

\"image-20210126224549229\"

\n
1
h = ad;                      //向上转型
\n

转型后的图:

\n

\"image-20210126224655825\"

\n

可以看到 h 经过 ad 最终指向的还是 Hero

\n

因此,把 h 指向 ADHero 一定可以,因为 ADHero 继承了 Hero,Hero能实现的功能 ADHero 也可以

\n

父类转子类(向下转型)

父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。
强制转换的意思就是:转换有风险,风险自担。

\n

假如 ADHeroSupportHero 的两个不同子类:

\n
1
2
3
Hero h =new Hero();
ADHero ad = new ADHero();
Support s = new Support();
\n
1
h = s;            // 向上转型,一定可以
\n

向上转型一定可以,因为 h通过 s 最终还是指向 Hero

\n

\"image-20210126225703371\"

\n
1
ad = (ADHero)h;    //向下转型,不一定可以,需要强制转换
\n

虽然 ad 指向的 ADHeroHero 的子类,眨眼看来 ad 最终也是指向 Hero 的,但实际上指向的是 ADHero,ADHero与Hero还是有区别的,ADHero有的方法Hero不一定有

\n

而ad通过h最终指向的是 Hero,而不是 ADHero,因此会有风险(指向ADHero才不会有风险),只能强制转换。

\n

\"image-20210126230408061\"

\n

因为子类拥有的方法父类不一定有,因此 h 向下转型后,ad可能就没有了一些子类的方法

\n

没有继承关系的两个类

没有继承关系的两个类,互相转换,一定会失败

\n

实现类转换成接口(向上转型)

假如 AD 是一个接口,ADHero 类继承了它

\n
1
2
3
4
5
6
7
public class Test {
public static void main(String[] args) {
ADHero ad = new ADHero();

AD adi = ad; //向上转型
}
}
\n

转型练习

如下转换能否成功?如果不能,是哪一行会出错?为什么会出错?

\n
1
2
3
4
5
6
7
8
9
10
11
public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {
ADHero ad = new ADHero();
Hero h = ad;
AD adi = (AD) h;
APHero ap = (APHero) adi;
}
}
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
分析
第7行向上转型没问题
第8行,可以将h强制转换为AD,因为 AD 与 Hero 之间通过子类关联在了一起;因此 h 指向了AD,而通过 h -> ad -> ADHero -> AD,最终也是指向AD,虽有风险,但不会报错;
第9行,不能将 adi 强制转换为 APHero,因为 AD 与 APHero之间没有关联,因此ap本应该指向 APHero,但通过转换 ap -> adi -> AD,即最终指向了AD,因此不能进行转换,会报错
\n
\n

\"image-20210126234924701\"

\n

instanceof 语句

instanceof Hero 判断一个引用所指向的对象,是否是以下两种,返回 true 或 false

\n
    \n
  • Hero类型
  • \n
  • Hero的子类
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package charactor;

public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {
ADHero ad = new ADHero();
APHero ap = new APHero();

Hero h1= ad;
Hero h2= ap;

//判断引用h1指向的对象,是否是ADHero类型
System.out.println(h1 instanceof ADHero); \t//true

//判断引用h2指向的对象,是否是APHero类型
System.out.println(h2 instanceof APHero);\t//true

//判断引用h1指向的对象,是否是Hero的子类型
System.out.println(h1 instanceof Hero);\t\t//true
}
}
\n

重写

子类可以继承父类的对象方法

\n

在继承后,重复提供该方法,就叫做方法的重写

\n

又叫覆盖 override

\n
\n

父类Item有一个方法,叫做effect

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package property;

public class Item {
String name;
int price;

public void buy(){
System.out.println("购买");
}
public void effect() {
System.out.println("物品使用后,可以有效果");
}

}
\n

子类LifePotion继承Item,同时也提供了方法effect

\n
1
2
3
4
5
6
7
8
9
package property;

public class LifePotion extends Item{

public void effect(){ \t\t\t\t\t\t//重写effect()方法
System.out.println("血瓶使用后,可以回血");
}

}
\n

调用重写的方法
调用就会执行重写的方法,而不是从父类的方法

\n
\n

重写的优点:

\n

如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。

\n

但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能放弃继承Item,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动.

\n

这样就增加了开发时间和维护成本

\n

多态

操作符的多态

同一个操作符在不同情境下,具备不同的作用:

\n
    \n
  • 如果+号两侧都是整型,那么+代表 数字相加
  • \n
  • 如果+号两侧,任意一个是字符串,那么+代表字符串连接
  • \n
\n

类的多态

多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态(实际是子类重写了父类的方法,导致子类的方法与父类的方法不同,子类之间的方法也不同)

\n

类的多态即父类引用指向不同的子类对象,调用同一个方法时出现不同的效果

\n
\n

假设 Hero是 ADHeroAPHero 的父类

\n
1
2
Hero h1 = new ADHero();
Hero h2 = new APHero();
\n
\n

类的多态的条件:

\n
    \n
  1. 父类(接口)引用指向子类对象

    \n
  2. \n
  3. 调用的方法有重写

    \n
  4. \n
\n

使用类多态的好处

如果不使用多态,例如:

\n
\n

假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法
useLifePotion、useMagicPotion;
除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如
usePurityPotion、useGuard、useInvisiblePotion等等等等

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package charactor;

import property.LifePotion;
import property.MagicPotion;

public class Hero {
public String name;
protected float hp;

public void useLifePotion(LifePotion lp){
lp.effect();
}
public void useMagicPotion(MagicPotion mp){
mp.effect();
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";

LifePotion lp =new LifePotion();
MagicPotion mp =new MagicPotion();

garen.useLifePotion(lp);
garen.useMagicPotion(mp);

}

}
\n

这个时候采用多态来解决这个问题:

\n
\n

设计一个方法叫做useItem,其参数类型是Item

\n
    \n
  • 如果是使用血瓶,调用该方法
  • \n
  • 如果是使用魔瓶,还是调用该方法
  • \n
  • 无论英雄要使用什么样的物品,只需要一个方法 即可
  • \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package charactor;

import property.Item;
import property.LifePotion;
import property.MagicPotion;

public class Hero {
public String name;
protected float hp;

public void useItem(Item i){ //设计一个方法叫做useItem,其参数类型是Item
i.effect();
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";

LifePotion lp =new LifePotion();
MagicPotion mp =new MagicPotion();

garen.useItem(lp); //使用血瓶lp
garen.useItem(mp); //使用魔瓶mp

}

}
\n

隐藏

与重写类似:

\n
    \n
  • 重写,是子类覆盖父类的对象方法
  • \n
  • 隐藏,就是子类覆盖父类的类方法
  • \n
\n

如何隐藏呢:

\n
\n

父类有一个类方法 :battleWin

\n
1
2
3
4
5
6
7
8
9
10
11
package charactor;

public class Hero {
public String name;
protected float hp;

public static void battleWin(){ //类方法,静态方法
System.out.println("hero battle win");
}

}
\n

创建一个子类,隐藏父类方法 battleWin()

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package charactor;

public class ADHero extends Hero implements AD{

//隐藏父类的battleWin方法
public static void battleWin(){
System.out.println("ad hero battle win");
}

public static void main(String[] args) {
Hero.battleWin(); // 调用父类的方法
ADHero.battleWin();//调用子类的方法
}

}
\n

问:对于Hero h =new ADHero();

\n

h是父类类型的引用,但是指向一个子类对象h.battleWin(); 会调用父类的方法?还是子类的方法?

\n

答:当父类的引用指向一个子类对象时,执行的:

\n
    \n
  • 对象方法是子类的对象方法(因为重写)
  • \n
  • 类方法是父类的类方法(类方法不能被重写)
  • \n
\n
\n

super关键字

super()用于子类的构造函数内部;

\n

特点:

\n
    \n
  1. 实例化一个父类的时候,父类的构造方法会被自动调用(根据实例化方式来选择调用有参或无参的构造方法);

    \n
  2. \n
  3. 实例化一个子类的时候,若没有写 super()语句,会默认调用父类无参构造方法。

    \n
  4. \n
  5. 并且,父类和子类的构造方法都会被调用,且父类的构造方法被调用

    \n
  6. \n
\n
1
2
3
4
5
6
7
package superKeyWord;
//父类:
public class Hero{
public Hero(){
System.out.println("调用父类的构造方法");
}
}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package superKeyWord;

//子类
public class SuperTest extends Hero {
public SuperTest(){
System.out.println("调用子类构造方法");
}

public static void main(String[] args){
new Hero();\t\t\t\t\t\t\t//创建父类对象
System.out.println("---------------");
new SuperTest();\t\t\t\t\t//创建子类对象
}

}

/*输出:
调用父类的构造方法
---------------
调用父类的构造方法
调用子类构造方法
*/
\n

super调用父类带参构造方法

super() 相当于一个父类的对象,在子类中使用就类似于创建了一个父类的对象,会调用父类带参的构造方法

\n

superthis 类似,this是当前类的对象,super是父类的对象,两者都只能在方法内部使用

\n
1
2
3
4
5
6
7
8
9
10
package superKeyWord;

//父类:
public class Hero{
public Hero(String name){ \t\t\t//带参的构造方法

System.out.println("调用父类的构造方法"+name);

}
}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package superKeyWord;

public class SuperTest extends Hero {

// super("h2"); //super只能在方法内部使用

public SuperTest(){

super("h2"); //正确,super在方法内使用

System.out.println("调用子类构造方法");
}

public static void main(String[] args){
new Hero("h1");
System.out.println("---------------");
new SuperTest();
}

}

/*输出:
调用父类的构造方法h1
---------------
调用父类的构造方法h2
调用子类构造方法
*/
\n

super调用父类属性

1
2
3
4
5
6
7
8
//父类
package superKeyWord;

public class Hero{

int moveSpeed = 100;

}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//子类

package superKeyWord;

public class SuperTest extends Hero {

int moveSpeed = 200;

public void getMoveSpeed1(){
System.out.println(super.moveSpeed); //打印父类moveSpeed
}

public void getMoveSpeed2() {
System.out.println(this.moveSpeed); //打印子类moveSpeed
}

public static void main(String[] args){

SuperTest h = new SuperTest();
h.getMoveSpeed1(); //输出:100
h.getMoveSpeed2();\t\t\t\t//输出:200
}

}
\n

super调用父类方法

当子类重写了父类的方法后,super调用的依然是父类原本的方法

\n

Object类

Object类是所有类的父类,即声明一个类的时候,默认就继承了Object

\n

Object提供的一些方法

    \n
  • toString():

    \n

    返回当前对象的字符串表达
    通过 System.out.println()打印对象就是打印该对象的toString()返回值

    \n
    1
    2
    3
    4
    5
    6
    Hero h = new Hero();

    //下面两行等效
    System.out.println(h);
    System.out.println(h.toString());

    \n
  • \n
  • finalize():

    \n

    当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件

    \n

    当它被垃圾回收的时候,它的finalize() 方法就会被调用。

    \n

    finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用

    \n
    1
    2
    3
    4
    Hero h;

    h = new Hero();
    h = new Hero();
    \n

    执行第四行的时候,第三行的 Hero() 对象没了引用指向,就满足垃圾回收条件

    \n
  • \n
  • equals():

    \n

    equals() 用于判断两个对象的内容是否相同,假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package charactor;

    public class Hero {
    public String name;
    protected float hp;

    public boolean equals(Object o){
    if(o instanceof Hero){
    Hero h = (Hero) o;
    return this.hp == h.hp;
    }
    return false;
    }

    public static void main(String[] args) {
    Hero h1= new Hero();
    h1.hp = 300;
    Hero h2= new Hero();
    h2.hp = 400;
    Hero h3= new Hero();
    h3.hp = 300;

    System.out.println(h1.equals(h2));
    System.out.println(h1.equals(h3));
    }
    }
    \n
  • \n
\n
    \n
  • ==

    \n

    这不是Object的方法,但是用于判断两个对象是否相同,
    更准确的讲,用于判断两个引用,是否指向了同一个对象

    \n
  • \n
  • hashCode():

    \n

    返回对象的哈希值

    \n
  • \n
  • 线程同步方法

    \n
    1
    2
    3
    wait()
    notify()
    notifyAll()
    \n
  • \n
  • getClass:

    \n

    会返回一个对象的类对象

    \n
  • \n
\n

final修饰

final修饰类,方法,基本类型变量,引用的时候分别有不同的意思。

\n
    \n
  • 修饰类:表示该类不能被继承

    \n
    1
    2
    3
    public final class Hero{   //该类不能被继承

    }
    \n
  • \n
\n
    \n
  • 修饰方法:表示该方法不能被重写

    \n
    1
    2
    3
    public final void useItem(){

    }
    \n
  • \n
\n
    \n
  • 修饰基本变量:表示该变量只能被赋值一次

    \n
    1
    2
    3
    final int a;
    a = 1;
    a = 2; //报错
    \n
  • \n
\n
    \n
  • 修饰引用:表示该引用只有一次指向对象的机会

    \n
    1
    2
    3
    final Hero h;
    h = new Hero();
    h = new Hero(); //报错
    \n
  • \n
\n
\n

可以用 final 修饰变量使其作为常量使用

\n

常量指的是可以公开,直接访问,不会变化的值

\n
\n

抽象类

抽象类

在类中声明一个方法,这个方法没有实现体,是一个“空”方法

\n

这样的方法就叫抽象方法,使用修饰符abstract

\n
\n

注意:

\n
    \n
  • 当一个类有抽象方法的时候,该类必须被声明为抽象类

    \n
  • \n
  • 子类继承抽象类后,必须重写抽象方法(如果有的话),赋予其具体功能

    \n
  • \n
  • 抽象类可以没有抽象方法,可以有实体方法,但是有抽象方法时必须声明为抽象类
  • \n
  • 抽象类不能直接进行实例化,即不能创建抽象类的对象
  • \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
package charactor;

public abstract class Hero { //抽象类

String name;
float hp;
float armor;
int moveSpeed;

public abstract void attack();// 抽象方法attack

}
\n

抽象类与接口区别

    \n
  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  2. \n
  3. 接口中除了 staticfinal 变量,不能有其他变量,而抽象类中则不一定。
  4. \n
  5. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
  6. \n
  7. 接口方法默认修饰符是 public,抽象方法可以有 publicprotecteddefault 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!)。
  8. \n
  9. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
  10. \n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
抽象类接口
抽象类也是类,子类只能继承一个类子类可以实现多个接口
可以是public,protected,package,private只能是public
静态、非静态静态
final或非final的属性final的
\n
\n
\n

另外,抽象类和接口都可以有实体方法。 接口中的实体方法,叫做默认方法

\n
\n

内部类

内部类分为四种:

\n
    \n
  • 非静态内部类

    \n

    非静态内部类,只有一个外部类对象存在的时候,才有意义。也就是说,要调用非静态内部类的属性和方法,必须要先创建一个外部类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Hero{
    String name;

    class BattleScore{ //内部类
    int score;
    public void kill(){

    }
    }
    }
    \n

    调用内部类的途径:

    \n
    1
    2
    Hero h = new Hero();  //先创建外部类
    BattleScore bs = h.new BattleScore();
    \n
  • \n
\n
    \n
  • 静态内部类

    \n

    在一个类里面声明一个静态内部类,静态内部类的实例化不需要一个外部类的实例为基础,可以直接实例化。

    \n

    另外,静态内部类不能直接访问外部类的对象属性

    \n

    语法:

    \n
    1
    new 外部类.静态内部类();
    \n

    例:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Hero{
    String name;

    static class BattleScore{ //静态内部类
    int score;
    public void kill(){

    //静态内部类不能直接访问外部类的对象属性
    System.out.println(name + "kille a Hero");//报错
    }
    }
    }
    \n

    实例化:

    \n
    1
    Hero.BattleScore bs = new Hero.BattleScore();
    \n
  • \n
\n
    \n
  • 匿名类

    \n

    匿名类指的是在创建某个类的对象的同时实例化它,使代码更加简洁精练
    通常情况下,要使用一个接口或者抽象类,都必须创建一个子类,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
    既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做匿名类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package charactor;

    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack(); //抽象方法

    public static void main(String[] args) {

    Hero h = new Hero(){
    //当场实现attack方法
    public void attack() {
    System.out.println("新的进攻手段");
    }
    };
    h.attack();
    //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名

    System.out.println(h);
    }

    }
    \n

    注意:在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final,但在jdk8中,已经不需要强制修饰成final了,因为编译器偷偷的帮你加上了看不见的final

    \n
  • \n
  • 本地类

    \n

    本地类可以理解为有名字的匿名类

    \n
      \n
    • 内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。
    • \n
    • 本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package charactor;

    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack();

    public static void main(String[] args) {

    //在主方法里声明本地类
    //与匿名类的区别在于,本地类有了自定义的类名
    class SomeHero extends Hero{
    public void attack() {
    System.out.println( name+ " 新的进攻手段");
    }
    }

    SomeHero h =new SomeHero();
    h.name ="地卜师";
    h.attack();
    }

    }
    \n
  • \n
\n

默认方法

默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法

\n

例如:

\n
1
2
3
4
5
6
7
8
public interface Mortal {
public void die(); //抽象方法

default public void revive() { //具体方法

System.out.println("本英雄复活了");
}
}
\n
\n

为什么会有默认方法?

\n

假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的子类,都需要做改动(都要在子类中考虑重写revive的具体方法)。

\n

但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法

\n

通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类

\n
\n"},{"title":"16-文件IO","abbrlink":"4f0e7b50","date":"2021-02-16T02:11:35.000Z","description":"java中关于文件IO的操作","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n\n\n# 文件IO系列\n\n\n\n文件和文件夹都是用`File`代表\n\n\n\n## 文件对象\n\n### 创建一个文件对象\n\n> 注意:不是创建文件\n\n首先导入 `File`类\n\n```java\nimport java.io.File;\n```\n\n使用绝对路径或者相对路径创建File对象\n\n```java\n// 绝对路径\nFile f1 = new File(\"d:/LOLFolder\"); //这是一个文件夹\n\n// 相对路径, 相对于工作目录(项目目录)\nFile f2 = new File(\"LOL.exe\");\t\t//这是一个文件\n```\n\n```java\n// 把f1作为父目录创建文件对象\nFile f3 = new File(f1, \"LOL.exe\");\n```\n\n### 文件常用方法\n\n```java\nFile f = new File(\"d:/LOLFolder/skin/garen.ski\");\n```\n\n\n\n- 获取文件相关信息:\n\n```java\nf.exists();\t\t//判断文件是否存在\n\nf.isDirectory();\t//判断是否是文件夹\n\nf.isFile(); //判断是否是文件\n\nf.length(); //文件长度(单位字节bytes)\n\nlong time = f.lastModified(); //返回从1970-1-1 08:00:00 开始的秒数\nDate d = new Date(time);\n\n\nf.setLastModified(0);\t//设置文件修改时间为1970.1.1 08:00:00\n\n```\n\n\n\n- 文件、文件夹操作\n\n```java\nf.list();\n// 以【字符串数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)\n\nFile[]fs = f.listFiles();\n// 以【文件(后缀为Files)数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)\n\nf.getParent();\n// 以【字符串】形式返回文件所在文件夹\n\nf.getParentFile();\n// 以【文件】形式返回获取所在文件夹\n\nf.mkdir();\n// 创建文件夹,如果父文件夹skin不存在,创建就无效\n\nf.mkdirs();\n// 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹\n\nf.createNewFile();\n// 创建一个空文件,如果父文件夹skin不存在,就会抛出异常\n\nf.getParentFile().mkdirs();\n// 所以创建一个空文件之前,通常都会创建父目录\n\nf.listRoots();\n// 列出所有的盘符c: d: e: 等等\n\nf.delete();\n// 刪除文件\n\nf.deleteOnExit();\n// JVM结束的时候,刪除文件,常用于临时文件的删除\n```\n\n\n\n## 什么是流\n\n> 流(Stream)就是一系列的数据\n\n当不同的介质之间有数据交互的时候,JAVA就使用流来实现。\n数据源可以是**文件**,**数据库**,**网络**,甚至是**其他的程序**。\n\n比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流\n\n(输入输出是针对Java虚拟机JVM而言的,流入JVM叫输入,反之叫输出)\n\n- 输入流:InputStream\n- 输出流:OutputStream\n\n![什么是流](https://stepimagewm.how2j.cn/759.png)\n\n### 文件输入流\n\njava中通过 `FileInputStream()` 实现文件输入流。\n\n如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。\n\n目前代码只是建立了流,还没有开始读取,真正的读取在下个章节讲解。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n try {\n File f = new File(\"d:/lol.txt\");//创建文件对象\n \n // 创建基于文件的输入流,即将文件对象放入流中\n FileInputStream fis = new FileInputStream(f);\n // 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中\n \n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 文件输出流\n\n`FileOutputStream` :通过这个输出流,就可以吧数据从java的虚拟机中写入硬盘\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\n \npublic class TestStream {\n    public static void main(String[] args) {\n        File f =new File(\"d:/lol.txt\");\n        try {\n            FileOutputStream fos = new FileOutputStream(f);\n        } catch (FileNotFoundException e) {\n            e.printStackTrace();\n        }\n \n    }\n}\n```\n\n\n\n## 字节流\n\n字节流即:用于以字节的形式读取和写入数据\n\nInputStream:字节输入流\nOutputStream:字节输出流\n\n### ASCII码概念\n\n所有的数据存放在计算机中都是以数字的形式存放的。 所以**字母就需要转换为数字才能够存放**。\n\n比如A就对应的数字65,a对应的数字97. 不同的**字母和符号**对应不同的数字,就是一张码表。\n\n> ASCII是这样的一种码表。 只包含简单的英文字母、符号、数字等。 **不包含中文,德文,俄语等复杂**的。\n\n### 以字节流形式读取文件内容\n\n`InputStream`是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。\n\n`FileInputStream` 是InputStream 子类,以 FileInputStream 为例进行文件读取\n\n```java\npackage IOlearning.stream;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n\npublic class StreamTest {\n\n public static void main(String[] args) {\n try {\n \n File f = new File(\"d:/javaTest.txt\"); //文件javaTest.txt内容是abc...xyz\n\n // 创建基于文件的输入流\n FileInputStream fis = new FileInputStream(f);\n\n // 创建字节数组,其长度就是文件的长度\n byte[] all = new byte[(int) f.length()];\n \n fis.read(all); // 以字节流的形式读取文件所有内容到 all\n \n for (byte b : all) {\n System.out.print(b + \" \");\n// 打印出来是97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122\n }\n\n // 每次使用完流,都应该进行关闭\n fis.close();\n\n } catch (IOException e) {\n e.printStackTrace();\n }\n\n }\n}\n```\n\n### 以字节流的形式向文件写入数据\n\n`OutputStream` 是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。\n\n`FileOutputStream` 是OutputStream子类,以FileOutputStream 为例向文件写出数据\n\n> **注**: 若文件d:/lol2.txt不存在,写出操作会***自动创建***该文件。\n> 但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n try {\n // 准备文件javaTest2.txt, 其中的内容是空的\n File f = new File(\"d:/javaTest2.txt\");\n \n // 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y\n byte data[] = { 88, 89 };\n \n // 创建基于文件的输出流\n FileOutputStream fos = new FileOutputStream(f);\n \n fos.write(data);// 把数据写入到输出流,注意data是个数组\n \n // 关闭输出流\n fos.close();\n \n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 总结(利用流读取文件步骤)\n\n4个操作步骤:\n\n- 创建文件对象\n\n- 将文件放入对应的流(输入、输出流)\n- 操作流(读取、写入)\n- 关闭流(close)\n\n\n\n## 关闭流的方式\n\n### 在try里关闭\n\n```java\ntry {\n\n File f = new File(\"d:/javaTest.txt\"); \n FileInputStream fis = new FileInputStream(f);\n byte[] all = new byte[(int) f.length()];\n fis.read(all); \n\n //使用完流,进行关闭\n fis.close();\n\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个**弊端**:\n如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 **不推荐**使用\n\n### 在finally里关闭\n\n这是**标准的**关闭流的方式\n\n1. 首先把流的**引用声明**在try的外面,如果声明在try里面,其作用域无法抵达finally.\n\n2. 在finally关闭之前,要先判断该引用是否为空\n\n3. 关闭的时候,需要再一次进行try catch处理\n\n> 这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的[有隐患](#在try里关闭)的方式,因为不麻烦🤣\n\n下面是标准方式:\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:/lol.txt\");\n FileInputStream fis = null;\t\t//在try外部声明\n \n try {\n fis = new FileInputStream(f);\n byte[] all = new byte[(int) f.length()];\n fis.read(all);\n for (byte b : all) {\n System.out.println(b);\n }\n \n } catch (IOException e) {\n e.printStackTrace();\n } finally {\n \n // 在finally 里关闭流\n if (null != fis)\n try {\n \n fis.close();\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n }\n \n }\n}\n```\n\n### 使用try()的方式\n\n把流定义在try()里,当try、catch或者finally结束的时候,会自动关闭。注意区别[在try里关闭](#在try里关闭)\n\n这种编写代码的方式叫做 **try-with-resources**, 这是从JDK7开始支持的技术。\n\n> 所有的流,都实现了一个**接口**叫做 `AutoCloseable`,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:/lol.txt\");\n \n //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭\n try (FileInputStream fis = new FileInputStream(f)) {\n \n byte[] all = new byte[(int) f.length()];\n fis.read(all);\n for (byte b : all) {\n System.out.println(b);\n }\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n## 字符流\n\n上面所讲的 `InputStream` 与 `OutputStream` 是字节流;\n\n这里还有字符流,即专门用于以字符的形式读写数据:\n\n- Reader\n- Writer\n\n### 用字符流读取文件\n\n`FileReader` 是 Reader子类,以FileReader 为例进行文件读取\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n \n // 准备文件lol.txt其中的内容是AB\n File f = new File(\"d:/lol.txt\");\n \n // 创建基于文件的Reader\n try (FileReader fr = new FileReader(f)) {\n \n // 创建字符数组,其长度就是文件的长度\n char[] all = new char[(int) f.length()];\n \n // 以字符流的形式读取文件所有内容\n fr.read(all);\n \n for (char b : all) {\n // 打印出来是A B\n System.out.println(b);\n }\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### 用字符流把字符串写入文件\n\n`FileWriter` 是Writer的子类,以FileWriter 为例把字符串写入到文件\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n // 准备文件lol2.txt\n File f = new File(\"d:/lol2.txt\");\n // 创建基于文件的Writer\n try (FileWriter fr = new FileWriter(f)) {\n \n // 以字符流的形式把数据写入到文件中\n String data=\"abcdefg1234567890\";\n char[] cs = data.toCharArray();\n fr.write(cs);\n \n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n## 中文问题\n\n### 编码概念\n\nASCII 字符集只有256个字符,用 0-255 之间的数字来表示。包括大小写字母、数字以及少数特殊字符:如标点符号、货币符号等。\n\n对于大多数拉丁语言来说,这些字符已经够用。\n\n但是,许多亚洲和东方语言所用的字符远远不止256个字符。有些超过千个。\n\n因此,为了突破 ASCII 码字符数的限制,试图用新的编码方法来针对超过256个字符的语言编写计算机程序\n\n### 常见编码\n\n工作后经常接触的编码方式有如下几种:\n\n- ISO-8859-1/ASCII: 数字和西欧字母\n- GBK/GB2312/BIG5: 中文\n- UNICODE: 统一码,万国码\n\n其中\n\n- ISO-8859-1 包含 ASCII\n- GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。\n- UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中\n\n### UNICODE和UTF\n\n虽然UNICODE可以存储所有字符,但如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。因为1个Unicode字符就占用`4 bytes`\n\n倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间\n\n在这种情况下,就出现了UNICODE的各种**减肥**子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了**减肥还能保证健康**的效果\n\nUTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的**减肥效果**,一般说来UTF-8是比较常用的方式\n\nUTF-8,UTF-16和UTF-32 彼此的区别在此不作赘述,有兴趣的可以参考 [unicode-百度百科](http://baike.baidu.com/link?url=ty4mEX5hSfK2xAyPO8N2zgxTibBE59CShSb5yFxbVkBun_QVz65llOPEXOepgPeqe3AQDLt6LLjTayn6tioS4_#4)\n\n> UTF-8编码方式:数字和字母用一个字节, 汉字用3个字节。\n\n### Java采用的是Unicode\n\n写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。\n而这些中文字符采用的编码方式,都是使用UNICODE.\n\n例如: \"中\"字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。\n\n### 一个汉字使用不同编码方式的表现\n\n以字符 **中** 为例,查看其在不同编码方式下的值是多少\n\n```java\npackage stream;\n \nimport java.io.UnsupportedEncodingException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n String str = \"中\";\n showCode(str);\n }\n \n private static void showCode(String str) {\n String[] encodes = { \"BIG5\", \"GBK\", \"GB2312\", \"UTF-8\", \"UTF-16\", \"UTF-32\" };\n for (String encode : encodes) {\n showCode(str, encode);\n }\n \n }\n \n private static void showCode(String str, String encode) {\n try {\n System.out.printf(\"字符: \\\"%s\\\" 的在编码方式%s下的十六进制值是%n\", str, encode);\n byte[] bs = str.getBytes(encode);\n \n for (byte b : bs) {\n int i = b&0xff;\n System.out.print(Integer.toHexString(i) + \"\\t\");\n }\n System.out.println();\n System.out.println();\n } catch (UnsupportedEncodingException e) {\n System.out.printf(\"UnsupportedEncodingException: %s编码方式无法解析字符%s\\n\", encode, str);\n }\n }\n}\n```\n\n![image-20210210233822081](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210210233822081.png)\n\n### 用FileInputStream 字节流正确读取中文\n\n为了能够正确的读取中文内容\n\n1. 必须了解文本是以哪种编码方式保存字符的\n\n2. 使用字节流读取文本\n3. 使用对应的**编码方式去识别这些数字**,得到正确的字符。 \n\n如本例,一个文件中的内容是字符\"中\",编码方式是GBK,那么读出来的数据一定是D6D0。\n\n再使用GBK编码方式识别D6D0,就能正确的得到字符**中**\n\n> **注:** 在GBK的棋盘上找到的**中**字后,JVM会自动找到**中**在UNICODE这个棋盘上对应的数字,并且以[UNICODE上的数字保存在内存中](https://how2j.cn/k/io/io-encoding/695.html#step2486)。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:\\\\project\\\\j2se\\\\src\\\\test.txt\"); //该文件保存时的编码方式为GBK\n \n try (FileInputStream fis = new FileInputStream(f);) {\n \n byte[] all = new byte[(int) f.length()];\n \n fis.read(all);\t//使用字节流读取文本\n \n String str = new String(all,\"GBK\"); //解码,解码方式为GBK\n System.out.println(str);\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 用FileReader 字符流正确读取中文\n\nFileReader得到的是字符,所以一定是已经把字节**根据某种编码识别成为字符**了\n\n而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK\n\nFileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:\n\n```java\nnew InputStreamReader(new FileInputStream(f), Charset.forName(\"UTF-8\")); \n```\n\n\n\n## 缓存流\n\n以介质是硬盘为例,**字节流和字符流的弊端**:\n在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。\n\n为了解决以上弊端,采用缓存流。\n缓存流在读取的时候,**会一次性读较多的数据到缓存中**,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。\n\n就好比吃饭,**不用缓存就是每吃一口都到锅里去铲**。**用缓存就是先把饭盛到碗里**,碗里的吃完了,再到锅里去铲。\n\n缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区**达到一定的量**,才把这些数据,**一起写入到硬盘中去**。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作,提高速度。\n\n### 使用缓存流读取数据\n\n缓存字符输入流 `BufferedReader` 可以一次读取一行数据,但要注意,缓存流必须建立在一个存在的流的基础上\n\n> 先准备好文件 `d:/lol.txt`,文件内容如下:\n>\n> garen kill teemo\n> teemo revive after 1 minutes\n> teemo try to garen, but killed again\n\n```java\npackage stream;\n \nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n\n File f = new File(\"d:/lol.txt\");\n \n try (\n FileReader fr = new FileReader(f);\t\t// 创建文件字符流\n BufferedReader br = new BufferedReader(fr); // 缓存流必须建立在一个存在的流的基础上\n )\n {\n while (true) {\n String line = br.readLine();// 一次读一行\n if (line == null)\n break;\n System.out.println(line);\n }\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### 使用缓存流写入数据\n\n[之前](#以字节流的形式向文件写入数据)的 `FileOutputStream` 与 `FileWriter`要写入一串数据时,必须将数据转换为数组,一次只能写入一个字符。\n\n而`PrintWriter` 缓存字符输出流, 可以一次写入一行数据;\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.PrintWriter;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n // 向文件lol2.txt中写入三行语句\n File f = new File(\"d:/lol2.txt\");\n \n try (\n FileWriter fw = new FileWriter(f); // 创建文件字符流\n PrintWriter pw = new PrintWriter(fw); // 缓存流必须建立在一个存在的流的基础上 \n \n ) {\n pw.println(\"garen kill teemo\"); //写入一行数据\n pw.println(\"teemo revive after 1 minutes\");\n pw.println(\"teemo try to garen, but killed again\");\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### flush方法\n\n有的时候,需要**立即把数据写入到硬盘**,而不是等缓存满了才写出去。 这时候就需要用到`flush()`方法\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.PrintWriter;\npublic class TestStream {\n public static void main(String[] args) {\n //向文件lol2.txt中写入三行语句\n File f =new File(\"d:/lol2.txt\");\n \n try(\n FileWriter fr = new FileWriter(f); //创建文件字符流\n PrintWriter pw = new PrintWriter(fr); //缓存流必须建立在一个存在的流的基础上\n ) {\n pw.println(\"garen kill teemo\");\n pw.flush(); //强制把缓存中的数据写入硬盘,无论缓存是否已满\n pw.println(\"teemo revive after 1 minutes\");\n pw.flush();\n pw.println(\"teemo try to garen, but killed again\");\n pw.flush();\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n }\n}\n```\n\n\n\n## 数据流\n\n- DataInputStream 数据输入流\n- DataOutputStream 数据输出流\n\n### 直接读写字符串\n\n使用数据流的`writeUTF()`和`readUTF()` 可以进行数据的**格式化顺序读写**;\n如本例,通过DataOutputStream 向文件顺序写出【布尔值,整数和字符串】。 然后再通过DataInputStream 顺序读入这些数据。\n\n> **注:** 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException\n>\n> 因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。\n\n读取步骤:\n\n1. 创建输入流 `FileInputStream()`\n2. 创建数据输入流 `DataInputStream()`\n3. - 读取布尔值:`boolean b= dis.readBoolean();`\n - 读取整数:`int i = dis.readInt();`\n - 读取字符串:`String str = dis.readUTF();`\n\n```java\nFile f =new File(\"d:/lol.txt\");\ntry (\n FileInputStream fis = new FileInputStream(f);\n DataInputStream dis =new DataInputStream(fis);\n){\n boolean b= dis.readBoolean();\n int i = dis.readInt();\n String str = dis.readUTF();\n\n System.out.println(\"读取到布尔值:\"+b);\n System.out.println(\"读取到整数:\"+i);\n System.out.println(\"读取到字符串:\"+str);\n\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n写入步骤:\n\n1. 创建输出流:`FileOutputStream()`\n2. 创建数据输出流:`DataOutputStream()`\n3. - 写入布尔值true:`dos.writeBoolean(true)`\n - 写入整数:`dos.writeInt(123)`\n - 写入字符串:`dos.writeUTF(\"This is my string\")`\n\n```java\nFile f =new File(\"d:/lol.txt\");\ntry (\n FileOutputStream fos = new FileOutputStream(f);\n DataOutputStream dos =new DataOutputStream(fos);\n){\n dos.writeBoolean(true);\n dos.writeInt(300);\n dos.writeUTF(\"123 this is gareen\");\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n\n\n## 对象流\n\n### 序列化一个对象\n\n需要用到:\n\n- 对象输入流:`ObjectInputStream`\n- 对象输出流:`ObjectOutputStream`\n\n> 把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口\n\n```java\npackage charactor;\n \nimport java.io.Serializable;\n \npublic class Hero implements Serializable {\n //表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号\n private static final long serialVersionUID = 1L;\n public String name;\n public float hp;\n \n}\n```\n\n步骤:\n\n1. 创建一个Hero对象h,设置其名称为garen。\n\n```java\nHero h = new Hero();\nh.name = \"garen\";\nh.hp = 616;\n```\n\n2. 把该对象序列化(即写入)到一个文件`garen.lol`。\n\n```java\nFile f =new File(\"d:/garen.lol\");\n\ntry(\n//创建对象输出流\nFileOutputStream fos = new FileOutputStream(f);\nObjectOutputStream oos =new ObjectOutputStream(fos);\n\n) {\noos.writeObject(h);\n\n} catch (IOException e) {\n// TODO Auto-generated catch block\ne.printStackTrace();\n} \n```\n\n3. 然后再通过序列化把该文件转换为一个Hero对象\n\n```java\nFile f =new File(\"d:/garen.lol\");\n\ntry(\n//创建对象输入流\nFileInputStream fis = new FileInputStream(f);\nObjectInputStream ois =new ObjectInputStream(fis);\n){\n Hero h2 = (Hero) ois.readObject();\n\tSystem.out.println(h2.name);\n\tSystem.out.println(h2.hp);\n}catch (ClassNotFoundException e) {\n// TODO Auto-generated catch block\ne.printStackTrace();\n}\n```\n\n\n\n## System.in\n\n- System.out 是常用的在控制台输出数据的\n- System.in 可以从控制台输入数据\n\n```java\ntry (InputStream is = System.in;) {\n while (true) {\n // 敲入a,然后敲回车可以看到\n // 97 13 10\n // 97是a的ASCII码\n // 13 10分别对应回车换行\n int i = is.read();\n System.out.println(i);\n }\n} catch (IOException e) {\n e.printStackTrace();\n}\n\n```\n\n### Scanner读取字符串\n\n使用System.in.read虽然可以读取数据,但是很不方便;\n使用`Scanner`就可以**逐行**读取了\n\n```java\nimport java.util.Scanner; //使用前需要导入\n```\n\n\n\n```java\nScanner s = new Scanner(System.in);\n\nwhile(true){\n String line = s.nextLine();\n System.out.println(line);\n}\n```\n\n### Scanner从控制台读取整数\n\n```java\nScanner s = new Scanner(System.in);\nint a = s.nextInt();\nSystem.out.println(\"第一个整数:\" + a);\nint b = s.nextInt();\nSystem.out.println(\"第二个整数:\" + b);\n```\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/javaSE/16-文件IO.md","raw":"---\ntitle: 16-文件IO\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 4f0e7b50\ndate: 2021-02-16 10:11:35\ndescription: java中关于文件IO的操作\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n\n\n# 文件IO系列\n\n\n\n文件和文件夹都是用`File`代表\n\n\n\n## 文件对象\n\n### 创建一个文件对象\n\n> 注意:不是创建文件\n\n首先导入 `File`类\n\n```java\nimport java.io.File;\n```\n\n使用绝对路径或者相对路径创建File对象\n\n```java\n// 绝对路径\nFile f1 = new File(\"d:/LOLFolder\"); //这是一个文件夹\n\n// 相对路径, 相对于工作目录(项目目录)\nFile f2 = new File(\"LOL.exe\");\t\t//这是一个文件\n```\n\n```java\n// 把f1作为父目录创建文件对象\nFile f3 = new File(f1, \"LOL.exe\");\n```\n\n### 文件常用方法\n\n```java\nFile f = new File(\"d:/LOLFolder/skin/garen.ski\");\n```\n\n\n\n- 获取文件相关信息:\n\n```java\nf.exists();\t\t//判断文件是否存在\n\nf.isDirectory();\t//判断是否是文件夹\n\nf.isFile(); //判断是否是文件\n\nf.length(); //文件长度(单位字节bytes)\n\nlong time = f.lastModified(); //返回从1970-1-1 08:00:00 开始的秒数\nDate d = new Date(time);\n\n\nf.setLastModified(0);\t//设置文件修改时间为1970.1.1 08:00:00\n\n```\n\n\n\n- 文件、文件夹操作\n\n```java\nf.list();\n// 以【字符串数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)\n\nFile[]fs = f.listFiles();\n// 以【文件(后缀为Files)数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)\n\nf.getParent();\n// 以【字符串】形式返回文件所在文件夹\n\nf.getParentFile();\n// 以【文件】形式返回获取所在文件夹\n\nf.mkdir();\n// 创建文件夹,如果父文件夹skin不存在,创建就无效\n\nf.mkdirs();\n// 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹\n\nf.createNewFile();\n// 创建一个空文件,如果父文件夹skin不存在,就会抛出异常\n\nf.getParentFile().mkdirs();\n// 所以创建一个空文件之前,通常都会创建父目录\n\nf.listRoots();\n// 列出所有的盘符c: d: e: 等等\n\nf.delete();\n// 刪除文件\n\nf.deleteOnExit();\n// JVM结束的时候,刪除文件,常用于临时文件的删除\n```\n\n\n\n## 什么是流\n\n> 流(Stream)就是一系列的数据\n\n当不同的介质之间有数据交互的时候,JAVA就使用流来实现。\n数据源可以是**文件**,**数据库**,**网络**,甚至是**其他的程序**。\n\n比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流\n\n(输入输出是针对Java虚拟机JVM而言的,流入JVM叫输入,反之叫输出)\n\n- 输入流:InputStream\n- 输出流:OutputStream\n\n![什么是流](https://stepimagewm.how2j.cn/759.png)\n\n### 文件输入流\n\njava中通过 `FileInputStream()` 实现文件输入流。\n\n如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。\n\n目前代码只是建立了流,还没有开始读取,真正的读取在下个章节讲解。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n try {\n File f = new File(\"d:/lol.txt\");//创建文件对象\n \n // 创建基于文件的输入流,即将文件对象放入流中\n FileInputStream fis = new FileInputStream(f);\n // 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中\n \n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 文件输出流\n\n`FileOutputStream` :通过这个输出流,就可以吧数据从java的虚拟机中写入硬盘\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\n \npublic class TestStream {\n    public static void main(String[] args) {\n        File f =new File(\"d:/lol.txt\");\n        try {\n            FileOutputStream fos = new FileOutputStream(f);\n        } catch (FileNotFoundException e) {\n            e.printStackTrace();\n        }\n \n    }\n}\n```\n\n\n\n## 字节流\n\n字节流即:用于以字节的形式读取和写入数据\n\nInputStream:字节输入流\nOutputStream:字节输出流\n\n### ASCII码概念\n\n所有的数据存放在计算机中都是以数字的形式存放的。 所以**字母就需要转换为数字才能够存放**。\n\n比如A就对应的数字65,a对应的数字97. 不同的**字母和符号**对应不同的数字,就是一张码表。\n\n> ASCII是这样的一种码表。 只包含简单的英文字母、符号、数字等。 **不包含中文,德文,俄语等复杂**的。\n\n### 以字节流形式读取文件内容\n\n`InputStream`是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。\n\n`FileInputStream` 是InputStream 子类,以 FileInputStream 为例进行文件读取\n\n```java\npackage IOlearning.stream;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n\npublic class StreamTest {\n\n public static void main(String[] args) {\n try {\n \n File f = new File(\"d:/javaTest.txt\"); //文件javaTest.txt内容是abc...xyz\n\n // 创建基于文件的输入流\n FileInputStream fis = new FileInputStream(f);\n\n // 创建字节数组,其长度就是文件的长度\n byte[] all = new byte[(int) f.length()];\n \n fis.read(all); // 以字节流的形式读取文件所有内容到 all\n \n for (byte b : all) {\n System.out.print(b + \" \");\n// 打印出来是97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122\n }\n\n // 每次使用完流,都应该进行关闭\n fis.close();\n\n } catch (IOException e) {\n e.printStackTrace();\n }\n\n }\n}\n```\n\n### 以字节流的形式向文件写入数据\n\n`OutputStream` 是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。\n\n`FileOutputStream` 是OutputStream子类,以FileOutputStream 为例向文件写出数据\n\n> **注**: 若文件d:/lol2.txt不存在,写出操作会***自动创建***该文件。\n> 但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n try {\n // 准备文件javaTest2.txt, 其中的内容是空的\n File f = new File(\"d:/javaTest2.txt\");\n \n // 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y\n byte data[] = { 88, 89 };\n \n // 创建基于文件的输出流\n FileOutputStream fos = new FileOutputStream(f);\n \n fos.write(data);// 把数据写入到输出流,注意data是个数组\n \n // 关闭输出流\n fos.close();\n \n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 总结(利用流读取文件步骤)\n\n4个操作步骤:\n\n- 创建文件对象\n\n- 将文件放入对应的流(输入、输出流)\n- 操作流(读取、写入)\n- 关闭流(close)\n\n\n\n## 关闭流的方式\n\n### 在try里关闭\n\n```java\ntry {\n\n File f = new File(\"d:/javaTest.txt\"); \n FileInputStream fis = new FileInputStream(f);\n byte[] all = new byte[(int) f.length()];\n fis.read(all); \n\n //使用完流,进行关闭\n fis.close();\n\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个**弊端**:\n如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 **不推荐**使用\n\n### 在finally里关闭\n\n这是**标准的**关闭流的方式\n\n1. 首先把流的**引用声明**在try的外面,如果声明在try里面,其作用域无法抵达finally.\n\n2. 在finally关闭之前,要先判断该引用是否为空\n\n3. 关闭的时候,需要再一次进行try catch处理\n\n> 这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的[有隐患](#在try里关闭)的方式,因为不麻烦🤣\n\n下面是标准方式:\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:/lol.txt\");\n FileInputStream fis = null;\t\t//在try外部声明\n \n try {\n fis = new FileInputStream(f);\n byte[] all = new byte[(int) f.length()];\n fis.read(all);\n for (byte b : all) {\n System.out.println(b);\n }\n \n } catch (IOException e) {\n e.printStackTrace();\n } finally {\n \n // 在finally 里关闭流\n if (null != fis)\n try {\n \n fis.close();\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n }\n \n }\n}\n```\n\n### 使用try()的方式\n\n把流定义在try()里,当try、catch或者finally结束的时候,会自动关闭。注意区别[在try里关闭](#在try里关闭)\n\n这种编写代码的方式叫做 **try-with-resources**, 这是从JDK7开始支持的技术。\n\n> 所有的流,都实现了一个**接口**叫做 `AutoCloseable`,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:/lol.txt\");\n \n //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭\n try (FileInputStream fis = new FileInputStream(f)) {\n \n byte[] all = new byte[(int) f.length()];\n fis.read(all);\n for (byte b : all) {\n System.out.println(b);\n }\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n## 字符流\n\n上面所讲的 `InputStream` 与 `OutputStream` 是字节流;\n\n这里还有字符流,即专门用于以字符的形式读写数据:\n\n- Reader\n- Writer\n\n### 用字符流读取文件\n\n`FileReader` 是 Reader子类,以FileReader 为例进行文件读取\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n \n // 准备文件lol.txt其中的内容是AB\n File f = new File(\"d:/lol.txt\");\n \n // 创建基于文件的Reader\n try (FileReader fr = new FileReader(f)) {\n \n // 创建字符数组,其长度就是文件的长度\n char[] all = new char[(int) f.length()];\n \n // 以字符流的形式读取文件所有内容\n fr.read(all);\n \n for (char b : all) {\n // 打印出来是A B\n System.out.println(b);\n }\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### 用字符流把字符串写入文件\n\n`FileWriter` 是Writer的子类,以FileWriter 为例把字符串写入到文件\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n // 准备文件lol2.txt\n File f = new File(\"d:/lol2.txt\");\n // 创建基于文件的Writer\n try (FileWriter fr = new FileWriter(f)) {\n \n // 以字符流的形式把数据写入到文件中\n String data=\"abcdefg1234567890\";\n char[] cs = data.toCharArray();\n fr.write(cs);\n \n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n## 中文问题\n\n### 编码概念\n\nASCII 字符集只有256个字符,用 0-255 之间的数字来表示。包括大小写字母、数字以及少数特殊字符:如标点符号、货币符号等。\n\n对于大多数拉丁语言来说,这些字符已经够用。\n\n但是,许多亚洲和东方语言所用的字符远远不止256个字符。有些超过千个。\n\n因此,为了突破 ASCII 码字符数的限制,试图用新的编码方法来针对超过256个字符的语言编写计算机程序\n\n### 常见编码\n\n工作后经常接触的编码方式有如下几种:\n\n- ISO-8859-1/ASCII: 数字和西欧字母\n- GBK/GB2312/BIG5: 中文\n- UNICODE: 统一码,万国码\n\n其中\n\n- ISO-8859-1 包含 ASCII\n- GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。\n- UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中\n\n### UNICODE和UTF\n\n虽然UNICODE可以存储所有字符,但如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。因为1个Unicode字符就占用`4 bytes`\n\n倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间\n\n在这种情况下,就出现了UNICODE的各种**减肥**子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了**减肥还能保证健康**的效果\n\nUTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的**减肥效果**,一般说来UTF-8是比较常用的方式\n\nUTF-8,UTF-16和UTF-32 彼此的区别在此不作赘述,有兴趣的可以参考 [unicode-百度百科](http://baike.baidu.com/link?url=ty4mEX5hSfK2xAyPO8N2zgxTibBE59CShSb5yFxbVkBun_QVz65llOPEXOepgPeqe3AQDLt6LLjTayn6tioS4_#4)\n\n> UTF-8编码方式:数字和字母用一个字节, 汉字用3个字节。\n\n### Java采用的是Unicode\n\n写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。\n而这些中文字符采用的编码方式,都是使用UNICODE.\n\n例如: \"中\"字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。\n\n### 一个汉字使用不同编码方式的表现\n\n以字符 **中** 为例,查看其在不同编码方式下的值是多少\n\n```java\npackage stream;\n \nimport java.io.UnsupportedEncodingException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n String str = \"中\";\n showCode(str);\n }\n \n private static void showCode(String str) {\n String[] encodes = { \"BIG5\", \"GBK\", \"GB2312\", \"UTF-8\", \"UTF-16\", \"UTF-32\" };\n for (String encode : encodes) {\n showCode(str, encode);\n }\n \n }\n \n private static void showCode(String str, String encode) {\n try {\n System.out.printf(\"字符: \\\"%s\\\" 的在编码方式%s下的十六进制值是%n\", str, encode);\n byte[] bs = str.getBytes(encode);\n \n for (byte b : bs) {\n int i = b&0xff;\n System.out.print(Integer.toHexString(i) + \"\\t\");\n }\n System.out.println();\n System.out.println();\n } catch (UnsupportedEncodingException e) {\n System.out.printf(\"UnsupportedEncodingException: %s编码方式无法解析字符%s\\n\", encode, str);\n }\n }\n}\n```\n\n![image-20210210233822081](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210210233822081.png)\n\n### 用FileInputStream 字节流正确读取中文\n\n为了能够正确的读取中文内容\n\n1. 必须了解文本是以哪种编码方式保存字符的\n\n2. 使用字节流读取文本\n3. 使用对应的**编码方式去识别这些数字**,得到正确的字符。 \n\n如本例,一个文件中的内容是字符\"中\",编码方式是GBK,那么读出来的数据一定是D6D0。\n\n再使用GBK编码方式识别D6D0,就能正确的得到字符**中**\n\n> **注:** 在GBK的棋盘上找到的**中**字后,JVM会自动找到**中**在UNICODE这个棋盘上对应的数字,并且以[UNICODE上的数字保存在内存中](https://how2j.cn/k/io/io-encoding/695.html#step2486)。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:\\\\project\\\\j2se\\\\src\\\\test.txt\"); //该文件保存时的编码方式为GBK\n \n try (FileInputStream fis = new FileInputStream(f);) {\n \n byte[] all = new byte[(int) f.length()];\n \n fis.read(all);\t//使用字节流读取文本\n \n String str = new String(all,\"GBK\"); //解码,解码方式为GBK\n System.out.println(str);\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 用FileReader 字符流正确读取中文\n\nFileReader得到的是字符,所以一定是已经把字节**根据某种编码识别成为字符**了\n\n而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK\n\nFileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:\n\n```java\nnew InputStreamReader(new FileInputStream(f), Charset.forName(\"UTF-8\")); \n```\n\n\n\n## 缓存流\n\n以介质是硬盘为例,**字节流和字符流的弊端**:\n在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。\n\n为了解决以上弊端,采用缓存流。\n缓存流在读取的时候,**会一次性读较多的数据到缓存中**,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。\n\n就好比吃饭,**不用缓存就是每吃一口都到锅里去铲**。**用缓存就是先把饭盛到碗里**,碗里的吃完了,再到锅里去铲。\n\n缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区**达到一定的量**,才把这些数据,**一起写入到硬盘中去**。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作,提高速度。\n\n### 使用缓存流读取数据\n\n缓存字符输入流 `BufferedReader` 可以一次读取一行数据,但要注意,缓存流必须建立在一个存在的流的基础上\n\n> 先准备好文件 `d:/lol.txt`,文件内容如下:\n>\n> garen kill teemo\n> teemo revive after 1 minutes\n> teemo try to garen, but killed again\n\n```java\npackage stream;\n \nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n\n File f = new File(\"d:/lol.txt\");\n \n try (\n FileReader fr = new FileReader(f);\t\t// 创建文件字符流\n BufferedReader br = new BufferedReader(fr); // 缓存流必须建立在一个存在的流的基础上\n )\n {\n while (true) {\n String line = br.readLine();// 一次读一行\n if (line == null)\n break;\n System.out.println(line);\n }\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### 使用缓存流写入数据\n\n[之前](#以字节流的形式向文件写入数据)的 `FileOutputStream` 与 `FileWriter`要写入一串数据时,必须将数据转换为数组,一次只能写入一个字符。\n\n而`PrintWriter` 缓存字符输出流, 可以一次写入一行数据;\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.PrintWriter;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n // 向文件lol2.txt中写入三行语句\n File f = new File(\"d:/lol2.txt\");\n \n try (\n FileWriter fw = new FileWriter(f); // 创建文件字符流\n PrintWriter pw = new PrintWriter(fw); // 缓存流必须建立在一个存在的流的基础上 \n \n ) {\n pw.println(\"garen kill teemo\"); //写入一行数据\n pw.println(\"teemo revive after 1 minutes\");\n pw.println(\"teemo try to garen, but killed again\");\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### flush方法\n\n有的时候,需要**立即把数据写入到硬盘**,而不是等缓存满了才写出去。 这时候就需要用到`flush()`方法\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.PrintWriter;\npublic class TestStream {\n public static void main(String[] args) {\n //向文件lol2.txt中写入三行语句\n File f =new File(\"d:/lol2.txt\");\n \n try(\n FileWriter fr = new FileWriter(f); //创建文件字符流\n PrintWriter pw = new PrintWriter(fr); //缓存流必须建立在一个存在的流的基础上\n ) {\n pw.println(\"garen kill teemo\");\n pw.flush(); //强制把缓存中的数据写入硬盘,无论缓存是否已满\n pw.println(\"teemo revive after 1 minutes\");\n pw.flush();\n pw.println(\"teemo try to garen, but killed again\");\n pw.flush();\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n }\n}\n```\n\n\n\n## 数据流\n\n- DataInputStream 数据输入流\n- DataOutputStream 数据输出流\n\n### 直接读写字符串\n\n使用数据流的`writeUTF()`和`readUTF()` 可以进行数据的**格式化顺序读写**;\n如本例,通过DataOutputStream 向文件顺序写出【布尔值,整数和字符串】。 然后再通过DataInputStream 顺序读入这些数据。\n\n> **注:** 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException\n>\n> 因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。\n\n读取步骤:\n\n1. 创建输入流 `FileInputStream()`\n2. 创建数据输入流 `DataInputStream()`\n3. - 读取布尔值:`boolean b= dis.readBoolean();`\n - 读取整数:`int i = dis.readInt();`\n - 读取字符串:`String str = dis.readUTF();`\n\n```java\nFile f =new File(\"d:/lol.txt\");\ntry (\n FileInputStream fis = new FileInputStream(f);\n DataInputStream dis =new DataInputStream(fis);\n){\n boolean b= dis.readBoolean();\n int i = dis.readInt();\n String str = dis.readUTF();\n\n System.out.println(\"读取到布尔值:\"+b);\n System.out.println(\"读取到整数:\"+i);\n System.out.println(\"读取到字符串:\"+str);\n\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n写入步骤:\n\n1. 创建输出流:`FileOutputStream()`\n2. 创建数据输出流:`DataOutputStream()`\n3. - 写入布尔值true:`dos.writeBoolean(true)`\n - 写入整数:`dos.writeInt(123)`\n - 写入字符串:`dos.writeUTF(\"This is my string\")`\n\n```java\nFile f =new File(\"d:/lol.txt\");\ntry (\n FileOutputStream fos = new FileOutputStream(f);\n DataOutputStream dos =new DataOutputStream(fos);\n){\n dos.writeBoolean(true);\n dos.writeInt(300);\n dos.writeUTF(\"123 this is gareen\");\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n\n\n## 对象流\n\n### 序列化一个对象\n\n需要用到:\n\n- 对象输入流:`ObjectInputStream`\n- 对象输出流:`ObjectOutputStream`\n\n> 把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口\n\n```java\npackage charactor;\n \nimport java.io.Serializable;\n \npublic class Hero implements Serializable {\n //表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号\n private static final long serialVersionUID = 1L;\n public String name;\n public float hp;\n \n}\n```\n\n步骤:\n\n1. 创建一个Hero对象h,设置其名称为garen。\n\n```java\nHero h = new Hero();\nh.name = \"garen\";\nh.hp = 616;\n```\n\n2. 把该对象序列化(即写入)到一个文件`garen.lol`。\n\n```java\nFile f =new File(\"d:/garen.lol\");\n\ntry(\n//创建对象输出流\nFileOutputStream fos = new FileOutputStream(f);\nObjectOutputStream oos =new ObjectOutputStream(fos);\n\n) {\noos.writeObject(h);\n\n} catch (IOException e) {\n// TODO Auto-generated catch block\ne.printStackTrace();\n} \n```\n\n3. 然后再通过序列化把该文件转换为一个Hero对象\n\n```java\nFile f =new File(\"d:/garen.lol\");\n\ntry(\n//创建对象输入流\nFileInputStream fis = new FileInputStream(f);\nObjectInputStream ois =new ObjectInputStream(fis);\n){\n Hero h2 = (Hero) ois.readObject();\n\tSystem.out.println(h2.name);\n\tSystem.out.println(h2.hp);\n}catch (ClassNotFoundException e) {\n// TODO Auto-generated catch block\ne.printStackTrace();\n}\n```\n\n\n\n## System.in\n\n- System.out 是常用的在控制台输出数据的\n- System.in 可以从控制台输入数据\n\n```java\ntry (InputStream is = System.in;) {\n while (true) {\n // 敲入a,然后敲回车可以看到\n // 97 13 10\n // 97是a的ASCII码\n // 13 10分别对应回车换行\n int i = is.read();\n System.out.println(i);\n }\n} catch (IOException e) {\n e.printStackTrace();\n}\n\n```\n\n### Scanner读取字符串\n\n使用System.in.read虽然可以读取数据,但是很不方便;\n使用`Scanner`就可以**逐行**读取了\n\n```java\nimport java.util.Scanner; //使用前需要导入\n```\n\n\n\n```java\nScanner s = new Scanner(System.in);\n\nwhile(true){\n String line = s.nextLine();\n System.out.println(line);\n}\n```\n\n### Scanner从控制台读取整数\n\n```java\nScanner s = new Scanner(System.in);\nint a = s.nextInt();\nSystem.out.println(\"第一个整数:\" + a);\nint b = s.nextInt();\nSystem.out.println(\"第二个整数:\" + b);\n```\n\n\n\n\n\n\n\n\n\n\n\n","slug":"javaSE/16-文件IO","published":1,"updated":"2021-08-29T15:31:42.266Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6tx00hbakve9wq17mn4","content":"

文件IO系列

文件和文件夹都是用File代表

\n

文件对象

创建一个文件对象

\n

注意:不是创建文件

\n
\n

首先导入 File

\n
1
import java.io.File;
\n

使用绝对路径或者相对路径创建File对象

\n
1
2
3
4
5
// 绝对路径
File f1 = new File("d:/LOLFolder"); //这是一个文件夹

// 相对路径, 相对于工作目录(项目目录)
File f2 = new File("LOL.exe");\t\t//这是一个文件
\n
1
2
// 把f1作为父目录创建文件对象
File f3 = new File(f1, "LOL.exe");
\n

文件常用方法

1
File f = new File("d:/LOLFolder/skin/garen.ski");
\n
    \n
  • 获取文件相关信息:
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
f.exists();\t\t//判断文件是否存在

f.isDirectory();\t//判断是否是文件夹

f.isFile(); //判断是否是文件

f.length(); //文件长度(单位字节bytes)

long time = f.lastModified(); //返回从1970-1-1 08:00:00 开始的秒数
Date d = new Date(time);


f.setLastModified(0);\t//设置文件修改时间为1970.1.1 08:00:00

\n
    \n
  • 文件、文件夹操作
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
f.list();
// 以【字符串数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

File[]fs = f.listFiles();
// 以【文件(后缀为Files)数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

f.getParent();
// 以【字符串】形式返回文件所在文件夹

f.getParentFile();
// 以【文件】形式返回获取所在文件夹

f.mkdir();
// 创建文件夹,如果父文件夹skin不存在,创建就无效

f.mkdirs();
// 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹

f.createNewFile();
// 创建一个空文件,如果父文件夹skin不存在,就会抛出异常

f.getParentFile().mkdirs();
// 所以创建一个空文件之前,通常都会创建父目录

f.listRoots();
// 列出所有的盘符c: d: e: 等等

f.delete();
// 刪除文件

f.deleteOnExit();
// JVM结束的时候,刪除文件,常用于临时文件的删除
\n

什么是流

\n

流(Stream)就是一系列的数据

\n
\n

当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
数据源可以是文件数据库网络,甚至是其他的程序

\n

比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流

\n

(输入输出是针对Java虚拟机JVM而言的,流入JVM叫输入,反之叫输出)

\n
    \n
  • 输入流:InputStream
  • \n
  • 输出流:OutputStream
  • \n
\n

\"什么是流\"

\n

文件输入流

java中通过 FileInputStream() 实现文件输入流。

\n

如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。

\n

目前代码只是建立了流,还没有开始读取,真正的读取在下个章节讲解。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
try {
File f = new File("d:/lol.txt");//创建文件对象

// 创建基于文件的输入流,即将文件对象放入流中
FileInputStream fis = new FileInputStream(f);
// 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

文件输出流

FileOutputStream :通过这个输出流,就可以吧数据从java的虚拟机中写入硬盘

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package stream;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
 
public class TestStream {
    public static void main(String[] args) {
        File f =new File("d:/lol.txt");
        try {
            FileOutputStream fos = new FileOutputStream(f);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
 
    }
}
\n

字节流

字节流即:用于以字节的形式读取和写入数据

\n

InputStream:字节输入流
OutputStream:字节输出流

\n

ASCII码概念

所有的数据存放在计算机中都是以数字的形式存放的。 所以字母就需要转换为数字才能够存放

\n

比如A就对应的数字65,a对应的数字97. 不同的字母和符号对应不同的数字,就是一张码表。

\n
\n

ASCII是这样的一种码表。 只包含简单的英文字母、符号、数字等。 不包含中文,德文,俄语等复杂的。

\n
\n

以字节流形式读取文件内容

InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。

\n

FileInputStream 是InputStream 子类,以 FileInputStream 为例进行文件读取

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package IOlearning.stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class StreamTest {

public static void main(String[] args) {
try {

File f = new File("d:/javaTest.txt"); //文件javaTest.txt内容是abc...xyz

// 创建基于文件的输入流
FileInputStream fis = new FileInputStream(f);

// 创建字节数组,其长度就是文件的长度
byte[] all = new byte[(int) f.length()];

fis.read(all); // 以字节流的形式读取文件所有内容到 all

for (byte b : all) {
System.out.print(b + " ");
// 打印出来是97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
}

// 每次使用完流,都应该进行关闭
fis.close();

} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

以字节流的形式向文件写入数据

OutputStream 是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。

\n

FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据

\n
\n

: 若文件d:/lol2.txt不存在,写出操作会自动创建该文件。
但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package stream;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
try {
// 准备文件javaTest2.txt, 其中的内容是空的
File f = new File("d:/javaTest2.txt");

// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
byte data[] = { 88, 89 };

// 创建基于文件的输出流
FileOutputStream fos = new FileOutputStream(f);

fos.write(data);// 把数据写入到输出流,注意data是个数组

// 关闭输出流
fos.close();

} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

总结(利用流读取文件步骤)

4个操作步骤:

\n
    \n
  • 创建文件对象

    \n
  • \n
  • 将文件放入对应的流(输入、输出流)

    \n
  • \n
  • 操作流(读取、写入)
  • \n
  • 关闭流(close)
  • \n
\n

关闭流的方式

在try里关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
try {

File f = new File("d:/javaTest.txt");
FileInputStream fis = new FileInputStream(f);
byte[] all = new byte[(int) f.length()];
fis.read(all);

//使用完流,进行关闭
fis.close();

} catch (IOException e) {
e.printStackTrace();
}
\n

在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用

\n

在finally里关闭

这是标准的关闭流的方式

\n
    \n
  1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.

    \n
  2. \n
  3. 在finally关闭之前,要先判断该引用是否为空

    \n
  4. \n
  5. 关闭的时候,需要再一次进行try catch处理

    \n
  6. \n
\n
\n

这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患的方式,因为不麻烦🤣

\n
\n

下面是标准方式:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:/lol.txt");
FileInputStream fis = null;\t\t//在try外部声明

try {
fis = new FileInputStream(f);
byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}

} catch (IOException e) {
e.printStackTrace();
} finally {

// 在finally 里关闭流
if (null != fis)
try {

fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
}
\n

使用try()的方式

把流定义在try()里,当try、catch或者finally结束的时候,会自动关闭。注意区别在try里关闭

\n

这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术。

\n
\n

所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:/lol.txt");

//把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
try (FileInputStream fis = new FileInputStream(f)) {

byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

字符流

上面所讲的 InputStreamOutputStream 是字节流;

\n

这里还有字符流,即专门用于以字符的形式读写数据:

\n
    \n
  • Reader
  • \n
  • Writer
  • \n
\n

用字符流读取文件

FileReader 是 Reader子类,以FileReader 为例进行文件读取

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package stream;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {

// 准备文件lol.txt其中的内容是AB
File f = new File("d:/lol.txt");

// 创建基于文件的Reader
try (FileReader fr = new FileReader(f)) {

// 创建字符数组,其长度就是文件的长度
char[] all = new char[(int) f.length()];

// 以字符流的形式读取文件所有内容
fr.read(all);

for (char b : all) {
// 打印出来是A B
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

用字符流把字符串写入文件

FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
// 准备文件lol2.txt
File f = new File("d:/lol2.txt");
// 创建基于文件的Writer
try (FileWriter fr = new FileWriter(f)) {

// 以字符流的形式把数据写入到文件中
String data="abcdefg1234567890";
char[] cs = data.toCharArray();
fr.write(cs);

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

中文问题

编码概念

ASCII 字符集只有256个字符,用 0-255 之间的数字来表示。包括大小写字母、数字以及少数特殊字符:如标点符号、货币符号等。

\n

对于大多数拉丁语言来说,这些字符已经够用。

\n

但是,许多亚洲和东方语言所用的字符远远不止256个字符。有些超过千个。

\n

因此,为了突破 ASCII 码字符数的限制,试图用新的编码方法来针对超过256个字符的语言编写计算机程序

\n

常见编码

工作后经常接触的编码方式有如下几种:

\n
    \n
  • ISO-8859-1/ASCII: 数字和西欧字母
  • \n
  • GBK/GB2312/BIG5: 中文
  • \n
  • UNICODE: 统一码,万国码
  • \n
\n

其中

\n
    \n
  • ISO-8859-1 包含 ASCII
  • \n
  • GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
  • \n
  • UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中
  • \n
\n

UNICODE和UTF

虽然UNICODE可以存储所有字符,但如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。因为1个Unicode字符就占用4 bytes

\n

倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间

\n

在这种情况下,就出现了UNICODE的各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了减肥还能保证健康的效果

\n

UTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的减肥效果,一般说来UTF-8是比较常用的方式

\n

UTF-8,UTF-16和UTF-32 彼此的区别在此不作赘述,有兴趣的可以参考 unicode-百度百科

\n
\n

UTF-8编码方式:数字和字母用一个字节, 汉字用3个字节。

\n
\n

Java采用的是Unicode

写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。
而这些中文字符采用的编码方式,都是使用UNICODE.

\n

例如: “中”字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。

\n

一个汉字使用不同编码方式的表现

以字符 为例,查看其在不同编码方式下的值是多少

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package stream;

import java.io.UnsupportedEncodingException;

public class TestStream {

public static void main(String[] args) {
String str = "中";
showCode(str);
}

private static void showCode(String str) {
String[] encodes = { "BIG5", "GBK", "GB2312", "UTF-8", "UTF-16", "UTF-32" };
for (String encode : encodes) {
showCode(str, encode);
}

}

private static void showCode(String str, String encode) {
try {
System.out.printf("字符: \\"%s\\" 的在编码方式%s下的十六进制值是%n", str, encode);
byte[] bs = str.getBytes(encode);

for (byte b : bs) {
int i = b&0xff;
System.out.print(Integer.toHexString(i) + "\\t");
}
System.out.println();
System.out.println();
} catch (UnsupportedEncodingException e) {
System.out.printf("UnsupportedEncodingException: %s编码方式无法解析字符%s\\n", encode, str);
}
}
}
\n

\"image-20210210233822081\"

\n

用FileInputStream 字节流正确读取中文

为了能够正确的读取中文内容

\n
    \n
  1. 必须了解文本是以哪种编码方式保存字符的

    \n
  2. \n
  3. 使用字节流读取文本

    \n
  4. \n
  5. 使用对应的编码方式去识别这些数字,得到正确的字符。
  6. \n
\n

如本例,一个文件中的内容是字符”中”,编码方式是GBK,那么读出来的数据一定是D6D0。

\n

再使用GBK编码方式识别D6D0,就能正确的得到字符

\n
\n

注: 在GBK的棋盘上找到的字后,JVM会自动找到在UNICODE这个棋盘上对应的数字,并且以UNICODE上的数字保存在内存中

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:\\\\project\\\\j2se\\\\src\\\\test.txt"); //该文件保存时的编码方式为GBK

try (FileInputStream fis = new FileInputStream(f);) {

byte[] all = new byte[(int) f.length()];

fis.read(all);\t//使用字节流读取文本

String str = new String(all,"GBK"); //解码,解码方式为GBK
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

用FileReader 字符流正确读取中文

FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成为字符

\n

而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK

\n

FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:

\n
1
new InputStreamReader(new FileInputStream(f), Charset.forName("UTF-8")); 
\n

缓存流

以介质是硬盘为例,字节流和字符流的弊端
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。

\n

为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。

\n

就好比吃饭,不用缓存就是每吃一口都到锅里去铲用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲。

\n

缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作,提高速度。

\n

使用缓存流读取数据

缓存字符输入流 BufferedReader 可以一次读取一行数据,但要注意,缓存流必须建立在一个存在的流的基础上

\n
\n

先准备好文件 d:/lol.txt,文件内容如下:

\n

garen kill teemo
teemo revive after 1 minutes
teemo try to garen, but killed again

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package stream;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {

File f = new File("d:/lol.txt");

try (
FileReader fr = new FileReader(f);\t\t// 创建文件字符流
BufferedReader br = new BufferedReader(fr); // 缓存流必须建立在一个存在的流的基础上
)
{
while (true) {
String line = br.readLine();// 一次读一行
if (line == null)
break;
System.out.println(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

使用缓存流写入数据

之前FileOutputStreamFileWriter要写入一串数据时,必须将数据转换为数组,一次只能写入一个字符。

\n

PrintWriter 缓存字符输出流, 可以一次写入一行数据;

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class TestStream {

public static void main(String[] args) {
// 向文件lol2.txt中写入三行语句
File f = new File("d:/lol2.txt");

try (
FileWriter fw = new FileWriter(f); // 创建文件字符流
PrintWriter pw = new PrintWriter(fw); // 缓存流必须建立在一个存在的流的基础上

) {
pw.println("garen kill teemo"); //写入一行数据
pw.println("teemo revive after 1 minutes");
pw.println("teemo try to garen, but killed again");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

flush方法

有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush()方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class TestStream {
public static void main(String[] args) {
//向文件lol2.txt中写入三行语句
File f =new File("d:/lol2.txt");

try(
FileWriter fr = new FileWriter(f); //创建文件字符流
PrintWriter pw = new PrintWriter(fr); //缓存流必须建立在一个存在的流的基础上
) {
pw.println("garen kill teemo");
pw.flush(); //强制把缓存中的数据写入硬盘,无论缓存是否已满
pw.println("teemo revive after 1 minutes");
pw.flush();
pw.println("teemo try to garen, but killed again");
pw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
\n

数据流

    \n
  • DataInputStream 数据输入流
  • \n
  • DataOutputStream 数据输出流
  • \n
\n

直接读写字符串

使用数据流的writeUTF()readUTF() 可以进行数据的格式化顺序读写
如本例,通过DataOutputStream 向文件顺序写出【布尔值,整数和字符串】。 然后再通过DataInputStream 顺序读入这些数据。

\n
\n

注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException

\n

因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。

\n
\n

读取步骤:

\n
    \n
  1. 创建输入流 FileInputStream()
  2. \n
  3. 创建数据输入流 DataInputStream()
  4. \n
    • \n
    • 读取布尔值:boolean b= dis.readBoolean();
    • \n
    • 读取整数:int i = dis.readInt();
    • \n
    • 读取字符串:String str = dis.readUTF();
    • \n
    \n
  5. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
File f =new File("d:/lol.txt");
try (
FileInputStream fis = new FileInputStream(f);
DataInputStream dis =new DataInputStream(fis);
){
boolean b= dis.readBoolean();
int i = dis.readInt();
String str = dis.readUTF();

System.out.println("读取到布尔值:"+b);
System.out.println("读取到整数:"+i);
System.out.println("读取到字符串:"+str);

} catch (IOException e) {
e.printStackTrace();
}
\n

写入步骤:

\n
    \n
  1. 创建输出流:FileOutputStream()
  2. \n
  3. 创建数据输出流:DataOutputStream()
  4. \n
    • \n
    • 写入布尔值true:dos.writeBoolean(true)
    • \n
    • 写入整数:dos.writeInt(123)
    • \n
    • 写入字符串:dos.writeUTF("This is my string")
    • \n
    \n
  5. \n
\n
1
2
3
4
5
6
7
8
9
10
11
File f =new File("d:/lol.txt");
try (
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos =new DataOutputStream(fos);
){
dos.writeBoolean(true);
dos.writeInt(300);
dos.writeUTF("123 this is gareen");
} catch (IOException e) {
e.printStackTrace();
}
\n

对象流

序列化一个对象

需要用到:

\n
    \n
  • 对象输入流:ObjectInputStream
  • \n
  • 对象输出流:ObjectOutputStream
  • \n
\n
\n

把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口

\n
\n
1
2
3
4
5
6
7
8
9
10
11
package charactor;

import java.io.Serializable;

public class Hero implements Serializable {
//表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
private static final long serialVersionUID = 1L;
public String name;
public float hp;

}
\n

步骤:

\n
    \n
  1. 创建一个Hero对象h,设置其名称为garen。
  2. \n
\n
1
2
3
Hero h = new Hero();
h.name = "garen";
h.hp = 616;
\n
    \n
  1. 把该对象序列化(即写入)到一个文件garen.lol
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
File f =new File("d:/garen.lol");

try(
//创建对象输出流
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos =new ObjectOutputStream(fos);

) {
oos.writeObject(h);

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
\n
    \n
  1. 然后再通过序列化把该文件转换为一个Hero对象
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
File f =new File("d:/garen.lol");

try(
//创建对象输入流
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois =new ObjectInputStream(fis);
){
Hero h2 = (Hero) ois.readObject();
\tSystem.out.println(h2.name);
\tSystem.out.println(h2.hp);
}catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
\n

System.in

    \n
  • System.out 是常用的在控制台输出数据的
  • \n
  • System.in 可以从控制台输入数据
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
try (InputStream is = System.in;) {
while (true) {
// 敲入a,然后敲回车可以看到
// 97 13 10
// 97是a的ASCII码
// 13 10分别对应回车换行
int i = is.read();
System.out.println(i);
}
} catch (IOException e) {
e.printStackTrace();
}

\n

Scanner读取字符串

使用System.in.read虽然可以读取数据,但是很不方便;
使用Scanner就可以逐行读取了

\n
1
import java.util.Scanner;  //使用前需要导入
\n
1
2
3
4
5
6
Scanner s = new Scanner(System.in);

while(true){
String line = s.nextLine();
System.out.println(line);
}
\n

Scanner从控制台读取整数

1
2
3
4
5
Scanner s = new Scanner(System.in);
int a = s.nextInt();
System.out.println("第一个整数:" + a);
int b = s.nextInt();
System.out.println("第二个整数:" + b);
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

文件IO系列

文件和文件夹都是用File代表

\n

文件对象

创建一个文件对象

\n

注意:不是创建文件

\n
\n

首先导入 File

\n
1
import java.io.File;
\n

使用绝对路径或者相对路径创建File对象

\n
1
2
3
4
5
// 绝对路径
File f1 = new File("d:/LOLFolder"); //这是一个文件夹

// 相对路径, 相对于工作目录(项目目录)
File f2 = new File("LOL.exe");\t\t//这是一个文件
\n
1
2
// 把f1作为父目录创建文件对象
File f3 = new File(f1, "LOL.exe");
\n

文件常用方法

1
File f = new File("d:/LOLFolder/skin/garen.ski");
\n
    \n
  • 获取文件相关信息:
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
f.exists();\t\t//判断文件是否存在

f.isDirectory();\t//判断是否是文件夹

f.isFile(); //判断是否是文件

f.length(); //文件长度(单位字节bytes)

long time = f.lastModified(); //返回从1970-1-1 08:00:00 开始的秒数
Date d = new Date(time);


f.setLastModified(0);\t//设置文件修改时间为1970.1.1 08:00:00

\n
    \n
  • 文件、文件夹操作
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
f.list();
// 以【字符串数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

File[]fs = f.listFiles();
// 以【文件(后缀为Files)数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

f.getParent();
// 以【字符串】形式返回文件所在文件夹

f.getParentFile();
// 以【文件】形式返回获取所在文件夹

f.mkdir();
// 创建文件夹,如果父文件夹skin不存在,创建就无效

f.mkdirs();
// 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹

f.createNewFile();
// 创建一个空文件,如果父文件夹skin不存在,就会抛出异常

f.getParentFile().mkdirs();
// 所以创建一个空文件之前,通常都会创建父目录

f.listRoots();
// 列出所有的盘符c: d: e: 等等

f.delete();
// 刪除文件

f.deleteOnExit();
// JVM结束的时候,刪除文件,常用于临时文件的删除
\n

什么是流

\n

流(Stream)就是一系列的数据

\n
\n

当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
数据源可以是文件数据库网络,甚至是其他的程序

\n

比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流

\n

(输入输出是针对Java虚拟机JVM而言的,流入JVM叫输入,反之叫输出)

\n
    \n
  • 输入流:InputStream
  • \n
  • 输出流:OutputStream
  • \n
\n

\"什么是流\"

\n

文件输入流

java中通过 FileInputStream() 实现文件输入流。

\n

如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。

\n

目前代码只是建立了流,还没有开始读取,真正的读取在下个章节讲解。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
try {
File f = new File("d:/lol.txt");//创建文件对象

// 创建基于文件的输入流,即将文件对象放入流中
FileInputStream fis = new FileInputStream(f);
// 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

文件输出流

FileOutputStream :通过这个输出流,就可以吧数据从java的虚拟机中写入硬盘

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package stream;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
 
public class TestStream {
    public static void main(String[] args) {
        File f =new File("d:/lol.txt");
        try {
            FileOutputStream fos = new FileOutputStream(f);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
 
    }
}
\n

字节流

字节流即:用于以字节的形式读取和写入数据

\n

InputStream:字节输入流
OutputStream:字节输出流

\n

ASCII码概念

所有的数据存放在计算机中都是以数字的形式存放的。 所以字母就需要转换为数字才能够存放

\n

比如A就对应的数字65,a对应的数字97. 不同的字母和符号对应不同的数字,就是一张码表。

\n
\n

ASCII是这样的一种码表。 只包含简单的英文字母、符号、数字等。 不包含中文,德文,俄语等复杂的。

\n
\n

以字节流形式读取文件内容

InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。

\n

FileInputStream 是InputStream 子类,以 FileInputStream 为例进行文件读取

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package IOlearning.stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class StreamTest {

public static void main(String[] args) {
try {

File f = new File("d:/javaTest.txt"); //文件javaTest.txt内容是abc...xyz

// 创建基于文件的输入流
FileInputStream fis = new FileInputStream(f);

// 创建字节数组,其长度就是文件的长度
byte[] all = new byte[(int) f.length()];

fis.read(all); // 以字节流的形式读取文件所有内容到 all

for (byte b : all) {
System.out.print(b + " ");
// 打印出来是97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
}

// 每次使用完流,都应该进行关闭
fis.close();

} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

以字节流的形式向文件写入数据

OutputStream 是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。

\n

FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据

\n
\n

: 若文件d:/lol2.txt不存在,写出操作会自动创建该文件。
但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package stream;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
try {
// 准备文件javaTest2.txt, 其中的内容是空的
File f = new File("d:/javaTest2.txt");

// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
byte data[] = { 88, 89 };

// 创建基于文件的输出流
FileOutputStream fos = new FileOutputStream(f);

fos.write(data);// 把数据写入到输出流,注意data是个数组

// 关闭输出流
fos.close();

} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

总结(利用流读取文件步骤)

4个操作步骤:

\n
    \n
  • 创建文件对象

    \n
  • \n
  • 将文件放入对应的流(输入、输出流)

    \n
  • \n
  • 操作流(读取、写入)
  • \n
  • 关闭流(close)
  • \n
\n

关闭流的方式

在try里关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
try {

File f = new File("d:/javaTest.txt");
FileInputStream fis = new FileInputStream(f);
byte[] all = new byte[(int) f.length()];
fis.read(all);

//使用完流,进行关闭
fis.close();

} catch (IOException e) {
e.printStackTrace();
}
\n

在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用

\n

在finally里关闭

这是标准的关闭流的方式

\n
    \n
  1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.

    \n
  2. \n
  3. 在finally关闭之前,要先判断该引用是否为空

    \n
  4. \n
  5. 关闭的时候,需要再一次进行try catch处理

    \n
  6. \n
\n
\n

这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患的方式,因为不麻烦🤣

\n
\n

下面是标准方式:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:/lol.txt");
FileInputStream fis = null;\t\t//在try外部声明

try {
fis = new FileInputStream(f);
byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}

} catch (IOException e) {
e.printStackTrace();
} finally {

// 在finally 里关闭流
if (null != fis)
try {

fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
}
\n

使用try()的方式

把流定义在try()里,当try、catch或者finally结束的时候,会自动关闭。注意区别在try里关闭

\n

这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术。

\n
\n

所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:/lol.txt");

//把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
try (FileInputStream fis = new FileInputStream(f)) {

byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

字符流

上面所讲的 InputStreamOutputStream 是字节流;

\n

这里还有字符流,即专门用于以字符的形式读写数据:

\n
    \n
  • Reader
  • \n
  • Writer
  • \n
\n

用字符流读取文件

FileReader 是 Reader子类,以FileReader 为例进行文件读取

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package stream;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {

// 准备文件lol.txt其中的内容是AB
File f = new File("d:/lol.txt");

// 创建基于文件的Reader
try (FileReader fr = new FileReader(f)) {

// 创建字符数组,其长度就是文件的长度
char[] all = new char[(int) f.length()];

// 以字符流的形式读取文件所有内容
fr.read(all);

for (char b : all) {
// 打印出来是A B
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

用字符流把字符串写入文件

FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
// 准备文件lol2.txt
File f = new File("d:/lol2.txt");
// 创建基于文件的Writer
try (FileWriter fr = new FileWriter(f)) {

// 以字符流的形式把数据写入到文件中
String data="abcdefg1234567890";
char[] cs = data.toCharArray();
fr.write(cs);

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

中文问题

编码概念

ASCII 字符集只有256个字符,用 0-255 之间的数字来表示。包括大小写字母、数字以及少数特殊字符:如标点符号、货币符号等。

\n

对于大多数拉丁语言来说,这些字符已经够用。

\n

但是,许多亚洲和东方语言所用的字符远远不止256个字符。有些超过千个。

\n

因此,为了突破 ASCII 码字符数的限制,试图用新的编码方法来针对超过256个字符的语言编写计算机程序

\n

常见编码

工作后经常接触的编码方式有如下几种:

\n
    \n
  • ISO-8859-1/ASCII: 数字和西欧字母
  • \n
  • GBK/GB2312/BIG5: 中文
  • \n
  • UNICODE: 统一码,万国码
  • \n
\n

其中

\n
    \n
  • ISO-8859-1 包含 ASCII
  • \n
  • GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
  • \n
  • UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中
  • \n
\n

UNICODE和UTF

虽然UNICODE可以存储所有字符,但如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。因为1个Unicode字符就占用4 bytes

\n

倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间

\n

在这种情况下,就出现了UNICODE的各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了减肥还能保证健康的效果

\n

UTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的减肥效果,一般说来UTF-8是比较常用的方式

\n

UTF-8,UTF-16和UTF-32 彼此的区别在此不作赘述,有兴趣的可以参考 unicode-百度百科

\n
\n

UTF-8编码方式:数字和字母用一个字节, 汉字用3个字节。

\n
\n

Java采用的是Unicode

写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。
而这些中文字符采用的编码方式,都是使用UNICODE.

\n

例如: “中”字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。

\n

一个汉字使用不同编码方式的表现

以字符 为例,查看其在不同编码方式下的值是多少

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package stream;

import java.io.UnsupportedEncodingException;

public class TestStream {

public static void main(String[] args) {
String str = "中";
showCode(str);
}

private static void showCode(String str) {
String[] encodes = { "BIG5", "GBK", "GB2312", "UTF-8", "UTF-16", "UTF-32" };
for (String encode : encodes) {
showCode(str, encode);
}

}

private static void showCode(String str, String encode) {
try {
System.out.printf("字符: \\"%s\\" 的在编码方式%s下的十六进制值是%n", str, encode);
byte[] bs = str.getBytes(encode);

for (byte b : bs) {
int i = b&0xff;
System.out.print(Integer.toHexString(i) + "\\t");
}
System.out.println();
System.out.println();
} catch (UnsupportedEncodingException e) {
System.out.printf("UnsupportedEncodingException: %s编码方式无法解析字符%s\\n", encode, str);
}
}
}
\n

\"image-20210210233822081\"

\n

用FileInputStream 字节流正确读取中文

为了能够正确的读取中文内容

\n
    \n
  1. 必须了解文本是以哪种编码方式保存字符的

    \n
  2. \n
  3. 使用字节流读取文本

    \n
  4. \n
  5. 使用对应的编码方式去识别这些数字,得到正确的字符。
  6. \n
\n

如本例,一个文件中的内容是字符”中”,编码方式是GBK,那么读出来的数据一定是D6D0。

\n

再使用GBK编码方式识别D6D0,就能正确的得到字符

\n
\n

注: 在GBK的棋盘上找到的字后,JVM会自动找到在UNICODE这个棋盘上对应的数字,并且以UNICODE上的数字保存在内存中

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:\\\\project\\\\j2se\\\\src\\\\test.txt"); //该文件保存时的编码方式为GBK

try (FileInputStream fis = new FileInputStream(f);) {

byte[] all = new byte[(int) f.length()];

fis.read(all);\t//使用字节流读取文本

String str = new String(all,"GBK"); //解码,解码方式为GBK
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

用FileReader 字符流正确读取中文

FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成为字符

\n

而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK

\n

FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:

\n
1
new InputStreamReader(new FileInputStream(f), Charset.forName("UTF-8")); 
\n

缓存流

以介质是硬盘为例,字节流和字符流的弊端
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。

\n

为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。

\n

就好比吃饭,不用缓存就是每吃一口都到锅里去铲用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲。

\n

缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作,提高速度。

\n

使用缓存流读取数据

缓存字符输入流 BufferedReader 可以一次读取一行数据,但要注意,缓存流必须建立在一个存在的流的基础上

\n
\n

先准备好文件 d:/lol.txt,文件内容如下:

\n

garen kill teemo
teemo revive after 1 minutes
teemo try to garen, but killed again

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package stream;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {

File f = new File("d:/lol.txt");

try (
FileReader fr = new FileReader(f);\t\t// 创建文件字符流
BufferedReader br = new BufferedReader(fr); // 缓存流必须建立在一个存在的流的基础上
)
{
while (true) {
String line = br.readLine();// 一次读一行
if (line == null)
break;
System.out.println(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

使用缓存流写入数据

之前FileOutputStreamFileWriter要写入一串数据时,必须将数据转换为数组,一次只能写入一个字符。

\n

PrintWriter 缓存字符输出流, 可以一次写入一行数据;

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class TestStream {

public static void main(String[] args) {
// 向文件lol2.txt中写入三行语句
File f = new File("d:/lol2.txt");

try (
FileWriter fw = new FileWriter(f); // 创建文件字符流
PrintWriter pw = new PrintWriter(fw); // 缓存流必须建立在一个存在的流的基础上

) {
pw.println("garen kill teemo"); //写入一行数据
pw.println("teemo revive after 1 minutes");
pw.println("teemo try to garen, but killed again");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

flush方法

有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush()方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class TestStream {
public static void main(String[] args) {
//向文件lol2.txt中写入三行语句
File f =new File("d:/lol2.txt");

try(
FileWriter fr = new FileWriter(f); //创建文件字符流
PrintWriter pw = new PrintWriter(fr); //缓存流必须建立在一个存在的流的基础上
) {
pw.println("garen kill teemo");
pw.flush(); //强制把缓存中的数据写入硬盘,无论缓存是否已满
pw.println("teemo revive after 1 minutes");
pw.flush();
pw.println("teemo try to garen, but killed again");
pw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
\n

数据流

    \n
  • DataInputStream 数据输入流
  • \n
  • DataOutputStream 数据输出流
  • \n
\n

直接读写字符串

使用数据流的writeUTF()readUTF() 可以进行数据的格式化顺序读写
如本例,通过DataOutputStream 向文件顺序写出【布尔值,整数和字符串】。 然后再通过DataInputStream 顺序读入这些数据。

\n
\n

注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException

\n

因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。

\n
\n

读取步骤:

\n
    \n
  1. 创建输入流 FileInputStream()
  2. \n
  3. 创建数据输入流 DataInputStream()
  4. \n
    • \n
    • 读取布尔值:boolean b= dis.readBoolean();
    • \n
    • 读取整数:int i = dis.readInt();
    • \n
    • 读取字符串:String str = dis.readUTF();
    • \n
    \n
  5. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
File f =new File("d:/lol.txt");
try (
FileInputStream fis = new FileInputStream(f);
DataInputStream dis =new DataInputStream(fis);
){
boolean b= dis.readBoolean();
int i = dis.readInt();
String str = dis.readUTF();

System.out.println("读取到布尔值:"+b);
System.out.println("读取到整数:"+i);
System.out.println("读取到字符串:"+str);

} catch (IOException e) {
e.printStackTrace();
}
\n

写入步骤:

\n
    \n
  1. 创建输出流:FileOutputStream()
  2. \n
  3. 创建数据输出流:DataOutputStream()
  4. \n
    • \n
    • 写入布尔值true:dos.writeBoolean(true)
    • \n
    • 写入整数:dos.writeInt(123)
    • \n
    • 写入字符串:dos.writeUTF("This is my string")
    • \n
    \n
  5. \n
\n
1
2
3
4
5
6
7
8
9
10
11
File f =new File("d:/lol.txt");
try (
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos =new DataOutputStream(fos);
){
dos.writeBoolean(true);
dos.writeInt(300);
dos.writeUTF("123 this is gareen");
} catch (IOException e) {
e.printStackTrace();
}
\n

对象流

序列化一个对象

需要用到:

\n
    \n
  • 对象输入流:ObjectInputStream
  • \n
  • 对象输出流:ObjectOutputStream
  • \n
\n
\n

把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口

\n
\n
1
2
3
4
5
6
7
8
9
10
11
package charactor;

import java.io.Serializable;

public class Hero implements Serializable {
//表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
private static final long serialVersionUID = 1L;
public String name;
public float hp;

}
\n

步骤:

\n
    \n
  1. 创建一个Hero对象h,设置其名称为garen。
  2. \n
\n
1
2
3
Hero h = new Hero();
h.name = "garen";
h.hp = 616;
\n
    \n
  1. 把该对象序列化(即写入)到一个文件garen.lol
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
File f =new File("d:/garen.lol");

try(
//创建对象输出流
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos =new ObjectOutputStream(fos);

) {
oos.writeObject(h);

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
\n
    \n
  1. 然后再通过序列化把该文件转换为一个Hero对象
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
File f =new File("d:/garen.lol");

try(
//创建对象输入流
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois =new ObjectInputStream(fis);
){
Hero h2 = (Hero) ois.readObject();
\tSystem.out.println(h2.name);
\tSystem.out.println(h2.hp);
}catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
\n

System.in

    \n
  • System.out 是常用的在控制台输出数据的
  • \n
  • System.in 可以从控制台输入数据
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
try (InputStream is = System.in;) {
while (true) {
// 敲入a,然后敲回车可以看到
// 97 13 10
// 97是a的ASCII码
// 13 10分别对应回车换行
int i = is.read();
System.out.println(i);
}
} catch (IOException e) {
e.printStackTrace();
}

\n

Scanner读取字符串

使用System.in.read虽然可以读取数据,但是很不方便;
使用Scanner就可以逐行读取了

\n
1
import java.util.Scanner;  //使用前需要导入
\n
1
2
3
4
5
6
Scanner s = new Scanner(System.in);

while(true){
String line = s.nextLine();
System.out.println(line);
}
\n

Scanner从控制台读取整数

1
2
3
4
5
Scanner s = new Scanner(System.in);
int a = s.nextInt();
System.out.println("第一个整数:" + a);
int b = s.nextInt();
System.out.println("第二个整数:" + b);
\n"}],"PostAsset":[],"PostCategory":[{"post_id":"cktk8o6og000vakve4p1e67aw","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6oj0014akvec2hqhwyu"},{"post_id":"cktk8o6og000vakve4p1e67aw","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6ol0019akve3a9nedjg"},{"post_id":"cktk8o6o40009akvehm657ia2","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6om001cakve6jks286m"},{"post_id":"cktk8o6o40009akvehm657ia2","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6oo001hakvebl2t8950"},{"post_id":"cktk8o6nw0001akve96rfdnwk","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6oq001kakve7f9nbg4e"},{"post_id":"cktk8o6nw0001akve96rfdnwk","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6ou001oakvef7m1avlf"},{"post_id":"cktk8o6o6000bakve2lmd5swc","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6ov001rakvea22maeyl"},{"post_id":"cktk8o6o6000bakve2lmd5swc","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6ow001vakvea67i0c4i"},{"post_id":"cktk8o6o9000gakve5fnphajo","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6ox001yakvec2amfe1j"},{"post_id":"cktk8o6o9000gakve5fnphajo","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6p40022akvefzqbco0t"},{"post_id":"cktk8o6nz0003akve5a7o3s93","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6p60025akve1fx4bsof"},{"post_id":"cktk8o6nz0003akve5a7o3s93","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6p8002aakve65yg6zu2"},{"post_id":"cktk8o6ob000jakve6uur2o8z","category_id":"cktk8o6ov001sakve83h63fm7","_id":"cktk8o6p9002dakve82hs3hgf"},{"post_id":"cktk8o6od000oakveeb9m24v2","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6pa002hakvecn1acudr"},{"post_id":"cktk8o6od000oakveeb9m24v2","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6pc002kakvefxwr9kpf"},{"post_id":"cktk8o6o30007akve3mey9pm8","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6pd002oakvedtxharhl"},{"post_id":"cktk8o6o30007akve3mey9pm8","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6pe002rakve52fe3teo"},{"post_id":"cktk8o6oe000rakve4wdsbjyw","category_id":"cktk8o6ov001sakve83h63fm7","_id":"cktk8o6pf002vakvef3qt7h1a"},{"post_id":"cktk8o6oi0011akve2ujw438x","category_id":"cktk8o6ov001sakve83h63fm7","_id":"cktk8o6pi0034akve343p7hbs"},{"post_id":"cktk8o6pc002makvec2g3cfzf","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qk005iakve0ndk6bl8"},{"post_id":"cktk8o6pc002makvec2g3cfzf","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6ql005lakve3z9ndia8"},{"post_id":"cktk8o6og000xakve4mebdy6p","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qm005oakve5hkw9h5n"},{"post_id":"cktk8o6og000xakve4mebdy6p","category_id":"cktk8o6qh005bakveacjzeuio","_id":"cktk8o6qn005sakve5vwg126m"},{"post_id":"cktk8o6pd002qakve0ic15g6q","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qp005xakve75rna0l3"},{"post_id":"cktk8o6pd002qakve0ic15g6q","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6qq0060akve288y7433"},{"post_id":"cktk8o6pe002takvedtbz9vw9","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qr0064akvecp3540c5"},{"post_id":"cktk8o6pe002takvedtbz9vw9","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6qs0067akvefp57gzbj"},{"post_id":"cktk8o6pf002xakve102r3c73","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qt006bakvedmok4bmg"},{"post_id":"cktk8o6pf002xakve102r3c73","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6qu006eakve3z5213g7"},{"post_id":"cktk8o6pg002zakve9wvvbwrm","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qv006iakved3f2hbpd"},{"post_id":"cktk8o6pg002zakve9wvvbwrm","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6qw006lakvees006wdv"},{"post_id":"cktk8o6oj0013akve45f3gq5i","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qy006pakvea6l7fwa2"},{"post_id":"cktk8o6oj0013akve45f3gq5i","category_id":"cktk8o6qs0068akve66aj2w3e","_id":"cktk8o6qy006sakvedowuh8yj"},{"post_id":"cktk8o6pi0032akve2d4z141s","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qz006wakveg55ndm47"},{"post_id":"cktk8o6pi0032akve2d4z141s","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6r1006yakve3ryy1253"},{"post_id":"cktk8o6pj0035akvehrre9dbw","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r20072akve8hwocydq"},{"post_id":"cktk8o6pj0035akvehrre9dbw","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6r30075akvecxsaby1t"},{"post_id":"cktk8o6ok0018akve39f5g6yx","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r4007aakveaoqffsfs"},{"post_id":"cktk8o6ok0018akve39f5g6yx","category_id":"cktk8o6qs0068akve66aj2w3e","_id":"cktk8o6r5007dakvefju87c73"},{"post_id":"cktk8o6pk0038akved9jw5nm3","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r6007hakve2r7g0cb9"},{"post_id":"cktk8o6pk0038akved9jw5nm3","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6r7007kakve6md255cy"},{"post_id":"cktk8o6pl003bakve3evtamml","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r8007oakve8nt917sa"},{"post_id":"cktk8o6pl003bakve3evtamml","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6r9007rakve1gtz1qyh"},{"post_id":"cktk8o6ol001bakve1lqxghp8","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rb007vakveeup23brs"},{"post_id":"cktk8o6ol001bakve1lqxghp8","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6rc007xakveabd7dwj5"},{"post_id":"cktk8o6pm003eakve82ox37f6","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rd0081akvebeht2p62"},{"post_id":"cktk8o6pm003eakve82ox37f6","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6re0084akvehojk4gzq"},{"post_id":"cktk8o6pn003hakvebel7gurk","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rf0089akvecsfx061p"},{"post_id":"cktk8o6pn003hakvebel7gurk","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6rg008bakve8tmz7tpr"},{"post_id":"cktk8o6oo001gakve5kd5ad6i","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rh008fakveeead0b0t"},{"post_id":"cktk8o6oo001gakve5kd5ad6i","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6rh008iakve39tz9l2h"},{"post_id":"cktk8o6po003kakveajnw0fjv","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rj008nakve41pf150g"},{"post_id":"cktk8o6po003kakveajnw0fjv","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6rk008qakve4c1858xj"},{"post_id":"cktk8o6pp003nakve3uzjgiks","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rl008takve5l5rgg4w"},{"post_id":"cktk8o6pp003nakve3uzjgiks","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6rl008vakve50hgd766"},{"post_id":"cktk8o6oq001jakvebouthuqu","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rm008yakve13rd38s6"},{"post_id":"cktk8o6oq001jakvebouthuqu","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6rn0090akvegoka2g7k"},{"post_id":"cktk8o6pq003pakveexb0ak5a","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ro0093akve37gm5y6d"},{"post_id":"cktk8o6pq003pakveexb0ak5a","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6ro0095akvec3593av6"},{"post_id":"cktk8o6pr003sakve2exe8imp","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rp0098akvegq5f80fe"},{"post_id":"cktk8o6pr003sakve2exe8imp","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6rp009aakvecf3ubpum"},{"post_id":"cktk8o6or001nakved1j01zfv","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rp009dakvea5u370pm"},{"post_id":"cktk8o6or001nakved1j01zfv","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6rq009fakvec4c2ci8i"},{"post_id":"cktk8o6ps003uakve66vo01gc","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rq009iakve27tbbabk"},{"post_id":"cktk8o6ps003uakve66vo01gc","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6rr009kakve1s5thkhh"},{"post_id":"cktk8o6pt003xakve53qs5vxp","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rr009nakve3yt32ssg"},{"post_id":"cktk8o6pt003xakve53qs5vxp","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6rs009pakvedy8k8cy2"},{"post_id":"cktk8o6ou001qakve1mo0aal1","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ru009sakve4soj6eqc"},{"post_id":"cktk8o6ou001qakve1mo0aal1","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6rv009uakveazdn6skv"},{"post_id":"cktk8o6pu003zakve3wlwdsem","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rx009xakve7wdi4ywd"},{"post_id":"cktk8o6pu003zakve3wlwdsem","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6ry009zakve2o6igl5f"},{"post_id":"cktk8o6pv0042akve76zr0o90","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6ry00a2akve4wblgq9f"},{"post_id":"cktk8o6pv0042akve76zr0o90","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6rz00a4akveatgobtqf"},{"post_id":"cktk8o6ow001uakve6hqegt4b","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6s000a6akvedf0c476i"},{"post_id":"cktk8o6ow001uakve6hqegt4b","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6s000a9akvegvax65sy"},{"post_id":"cktk8o6pw0044akveh5zqdjte","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s100abakveclkgefvx"},{"post_id":"cktk8o6pw0044akveh5zqdjte","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6s100adakve1uyedra5"},{"post_id":"cktk8o6px0047akve8seydg2u","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s200agakve0dwk82ox"},{"post_id":"cktk8o6px0047akve8seydg2u","category_id":"cktk8o6rz00a5akvehafeesla","_id":"cktk8o6s200aiakved1yw255w"},{"post_id":"cktk8o6ox001xakvegmtv0lsp","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6s300alakveb1sa8ry9"},{"post_id":"cktk8o6ox001xakvegmtv0lsp","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6s300aoakvehbzcggp2"},{"post_id":"cktk8o6py0049akve15r5eo74","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s400aqakved8ayh2cx"},{"post_id":"cktk8o6py0049akve15r5eo74","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6s400atakveeh754wak"},{"post_id":"cktk8o6py004cakve0g6zci7t","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s500avakvecb8x5os0"},{"post_id":"cktk8o6py004cakve0g6zci7t","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6s500ayakve6htn052r"},{"post_id":"cktk8o6oy0020akvegdxh7rqn","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6s500b0akvef2ny4vth"},{"post_id":"cktk8o6oy0020akvegdxh7rqn","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6s700b3akve545w7eu1"},{"post_id":"cktk8o6q0004eakve6dzd49z7","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s800b5akve0px41c1n"},{"post_id":"cktk8o6q0004eakve6dzd49z7","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6s800b8akvegfnz5ffg"},{"post_id":"cktk8o6q1004hakve7ci71f3m","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s900baakve0slnezxu"},{"post_id":"cktk8o6q1004hakve7ci71f3m","category_id":"cktk8o6rz00a5akvehafeesla","_id":"cktk8o6s900bdakvehmqk7ryb"},{"post_id":"cktk8o6p60024akvegkx6f24q","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sa00bfakvede71avgc"},{"post_id":"cktk8o6p60024akvegkx6f24q","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6sa00biakve44sz9l3k"},{"post_id":"cktk8o6q2004jakvedojmd1vx","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6sa00bkakve3r9d6xsx"},{"post_id":"cktk8o6q2004jakvedojmd1vx","category_id":"cktk8o6rz00a5akvehafeesla","_id":"cktk8o6sb00bnakvefbfxfuzl"},{"post_id":"cktk8o6q4004makve8gio9l5r","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sb00bqakve2o4h2u0i"},{"post_id":"cktk8o6q4004makve8gio9l5r","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6sc00bsakvec7oza7gp"},{"post_id":"cktk8o6p70028akve23cieqja","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sc00bvakve1l9uglyx"},{"post_id":"cktk8o6p70028akve23cieqja","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6sd00bxakvef85sbodq"},{"post_id":"cktk8o6q5004oakve8g0k1g9x","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6se00c0akvefw0r9pq1"},{"post_id":"cktk8o6q5004oakve8g0k1g9x","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6se00c2akvea2ze4kry"},{"post_id":"cktk8o6q6004rakve7m1j1plt","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sf00c5akve4bsm0ziq"},{"post_id":"cktk8o6q6004rakve7m1j1plt","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6sg00c7akve206h80uw"},{"post_id":"cktk8o6p8002cakvedimceatv","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sh00caakve0leo7zey"},{"post_id":"cktk8o6p8002cakvedimceatv","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6sh00ccakve188ba77o"},{"post_id":"cktk8o6q7004takvecc6ph7mb","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6si00cfakvefab977bu"},{"post_id":"cktk8o6q7004takvecc6ph7mb","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6si00chakve0fuv2dre"},{"post_id":"cktk8o6q9004wakve8yyz5ix5","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sj00ckakve1g9yarhv"},{"post_id":"cktk8o6q9004wakve8yyz5ix5","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6sj00cmakvedm3t4jiu"},{"post_id":"cktk8o6p9002fakve7ps4flkp","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sk00cpakve64ncfnjw"},{"post_id":"cktk8o6p9002fakve7ps4flkp","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6sk00crakve10p3d2yz"},{"post_id":"cktk8o6qb004yakve5qk6a50r","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sl00cuakve1crm94d5"},{"post_id":"cktk8o6qb004yakve5qk6a50r","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6sl00cwakve05qffyqm"},{"post_id":"cktk8o6qc0051akve1nql6qt7","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sm00czakvehqwsedkl"},{"post_id":"cktk8o6qc0051akve1nql6qt7","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6sm00d1akve7zyi46tw"},{"post_id":"cktk8o6pb002jakvefzhpc3tk","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6so00d4akvehnbbc4vq"},{"post_id":"cktk8o6pb002jakvefzhpc3tk","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6so00d6akve3pv156de"},{"post_id":"cktk8o6qd0053akve87tvdadq","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sp00d9akve5pau1now"},{"post_id":"cktk8o6qd0053akve87tvdadq","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6sp00dbakve0gd182ew"},{"post_id":"cktk8o6qe0056akvea9ks9ha1","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sq00deakvead4chvyt"},{"post_id":"cktk8o6qe0056akvea9ks9ha1","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6sq00dgakvehlqqbnir"},{"post_id":"cktk8o6qf0058akve50qfda62","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sq00djakvebfpncx6n"},{"post_id":"cktk8o6qf0058akve50qfda62","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6sr00dlakve9i01fx4u"},{"post_id":"cktk8o6qg005aakveh3vd1y50","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sr00dnakveatrt5zjy"},{"post_id":"cktk8o6qg005aakveh3vd1y50","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6ss00dqakve9x6nh8id"},{"post_id":"cktk8o6qj005dakve37dy5lne","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ss00dsakvecun1ebgg"},{"post_id":"cktk8o6qj005dakve37dy5lne","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6ss00dvakvefm0w4z15"},{"post_id":"cktk8o6qj005fakvegpvj86dz","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6st00dxakve9sve4p0j"},{"post_id":"cktk8o6qj005fakvegpvj86dz","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6st00e0akvefdv62me0"},{"post_id":"cktk8o6ql005jakve8xo8cwcv","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6su00e2akve5byn9ejm"},{"post_id":"cktk8o6ql005jakve8xo8cwcv","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6sx00e5akved6gu0o54"},{"post_id":"cktk8o6ql005makve7a1dc654","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sy00e7akvecj5d430r"},{"post_id":"cktk8o6ql005makve7a1dc654","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6sz00eaakvecy9nfta8"},{"post_id":"cktk8o6qn005qakve26rcd19s","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t400ecakve6v23dpmg"},{"post_id":"cktk8o6qn005qakve26rcd19s","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t400eeakvefw6i8me2"},{"post_id":"cktk8o6qn005takveawfj22pb","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t500ehakvefpgo453g"},{"post_id":"cktk8o6qn005takveawfj22pb","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t500ejakve196reb6y"},{"post_id":"cktk8o6qp005wakvedimp04vg","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t600emakve4bn4aa02"},{"post_id":"cktk8o6qp005wakvedimp04vg","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t600eoakve06ijbfyn"},{"post_id":"cktk8o6qq005zakve2q775ly3","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t700erakvefcgw2gl7"},{"post_id":"cktk8o6qq005zakve2q775ly3","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t700etakve7x6u5y46"},{"post_id":"cktk8o6qr0063akvedrb617kz","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t800ewakve4zw7fd9w"},{"post_id":"cktk8o6qr0063akvedrb617kz","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t800exakvee5c977vg"},{"post_id":"cktk8o6qs0066akve3ppb6e1q","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t800f0akve33ocdtke"},{"post_id":"cktk8o6qs0066akve3ppb6e1q","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t800f1akved74ka716"},{"post_id":"cktk8o6qt006aakvealgt52ey","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t900f3akve3f7c4tfw"},{"post_id":"cktk8o6qt006aakvealgt52ey","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t900f4akvehcpj04vk"},{"post_id":"cktk8o6qu006dakve8q0q3khe","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t900f6akvec70ubym2"},{"post_id":"cktk8o6qu006dakve8q0q3khe","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t900f7akve4ed5a69k"},{"post_id":"cktk8o6qv006hakve2qm4crpn","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ta00f9akvebawxa825"},{"post_id":"cktk8o6qv006hakve2qm4crpn","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6ta00faakvefml23gpd"},{"post_id":"cktk8o6qw006kakvec7h0d155","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ta00fcakve7dqwh5is"},{"post_id":"cktk8o6qw006kakvec7h0d155","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6ta00fdakve7r385sl4"},{"post_id":"cktk8o6qx006oakveh6kj9q64","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ta00ffakve6h0ndec6"},{"post_id":"cktk8o6qx006oakveh6kj9q64","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tb00fgakvegqcjh9ur"},{"post_id":"cktk8o6qy006rakve1z853ljh","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tb00fiakveakcceace"},{"post_id":"cktk8o6qy006rakve1z853ljh","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tb00fjakve32z7goif"},{"post_id":"cktk8o6qz006uakve9nqxfhzr","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tb00flakve5n9th5kp"},{"post_id":"cktk8o6qz006uakve9nqxfhzr","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tb00fmakved1qf5tq4"},{"post_id":"cktk8o6r0006xakve9680bvvn","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tc00foakve4ujj0bni"},{"post_id":"cktk8o6r0006xakve9680bvvn","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tc00fpakve24jo659q"},{"post_id":"cktk8o6r10070akvedu2zcjwq","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tc00frakvegtsa3uqm"},{"post_id":"cktk8o6r10070akvedu2zcjwq","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tc00fsakve75xialww"},{"post_id":"cktk8o6r20074akve5rp09eoj","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6td00fuakveaqxu6ze2"},{"post_id":"cktk8o6r20074akve5rp09eoj","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6td00fvakve78e72sk6"},{"post_id":"cktk8o6r30078akvehflj72w7","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6td00fxakve4tvabr2v"},{"post_id":"cktk8o6r30078akvehflj72w7","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6td00fyakvegw541tad"},{"post_id":"cktk8o6r5007cakve2yiob0j3","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6td00g0akvefnlue3z8"},{"post_id":"cktk8o6r5007cakve2yiob0j3","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6te00g1akvegp4l3r08"},{"post_id":"cktk8o6r5007fakve7bba4t62","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6te00g3akvehksf7yy2"},{"post_id":"cktk8o6r5007fakve7bba4t62","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6te00g4akve4sdf5wkn"},{"post_id":"cktk8o6r6007jakvegi6bdbyo","category_id":"cktk8o6td00fzakve55x24m4x","_id":"cktk8o6te00g6akvee17p0sm2"},{"post_id":"cktk8o6r7007lakve2vjjaimr","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tf00g8akvefh3l9yxw"},{"post_id":"cktk8o6r7007lakve2vjjaimr","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tf00g9akvee7ah76yg"},{"post_id":"cktk8o6r8007qakvebtub8ep8","category_id":"cktk8o6td00fzakve55x24m4x","_id":"cktk8o6tf00gbakve1cq302x0"},{"post_id":"cktk8o6r9007sakvea9jb34oj","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6tf00gdakve4zmtbi2u"},{"post_id":"cktk8o6r9007sakvea9jb34oj","category_id":"cktk8o6te00g7akvedfi64j0n","_id":"cktk8o6tg00geakve1cgz3vh4"},{"post_id":"cktk8o6rb007wakve7c6t6uot","category_id":"cktk8o6td00fzakve55x24m4x","_id":"cktk8o6tg00ggakve73vz7ott"},{"post_id":"cktk8o6rc007yakve8853hjrp","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6tg00giakved18646cx"},{"post_id":"cktk8o6rc007yakve8853hjrp","category_id":"cktk8o6te00g7akvedfi64j0n","_id":"cktk8o6th00gjakve5n34ayg7"},{"post_id":"cktk8o6rd0083akve82iv1jsm","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6th00glakve8ny79chw"},{"post_id":"cktk8o6rd0083akve82iv1jsm","category_id":"cktk8o6te00g7akvedfi64j0n","_id":"cktk8o6th00gmakveexqo8pa5"},{"post_id":"cktk8o6re0086akve1a6d4ffa","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6th00gnakveb44wezxq"},{"post_id":"cktk8o6re0086akve1a6d4ffa","category_id":"cktk8o6te00g7akvedfi64j0n","_id":"cktk8o6th00gpakve47l20r49"},{"post_id":"cktk8o6rf008aakve5eeddxx1","category_id":"cktk8o6th00gkakve2e1eb0kc","_id":"cktk8o6th00gqakvec87uhpbu"},{"post_id":"cktk8o6rh008hakveh2yhheff","category_id":"cktk8o6th00grakve7iws2msc","_id":"cktk8o6ti00guakveeza12uof"},{"post_id":"cktk8o6ri008kakve8zmh4jwv","category_id":"cktk8o6th00grakve7iws2msc","_id":"cktk8o6ti00gvakvehtlo554s"},{"post_id":"cktk8o6rj008oakvef3va6zbp","category_id":"cktk8o6ti00gtakve51ks4u05","_id":"cktk8o6tj00gxakve02ad9rcf"},{"post_id":"cktk8o6rg008cakve0mvn1u4x","category_id":"cktk8o6th00goakve1pju20c4","_id":"cktk8o6tj00gyakve1wwehem0"},{"post_id":"cktk8o6rg008cakve0mvn1u4x","category_id":"cktk8o6ti00gwakve0x2ufj97","_id":"cktk8o6tj00gzakve9vjgfnsd"},{"post_id":"cktk8o6tt00h0akvehg5w1fi3","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6tw00h6akved2p631x6"},{"post_id":"cktk8o6tt00h0akvehg5w1fi3","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6tw00h9akve9hz2d46f"},{"post_id":"cktk8o6tu00h1akve7d054dj8","category_id":"cktk8o6ov001sakve83h63fm7","_id":"cktk8o6ty00hcakvecs0narij"},{"post_id":"cktk8o6tu00h3akve8scabvi5","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tz00heakveduta8x9y"},{"post_id":"cktk8o6tu00h3akve8scabvi5","category_id":"cktk8o6qh005bakveacjzeuio","_id":"cktk8o6tz00hgakvegnye9o21"},{"post_id":"cktk8o6tv00h5akvefji7akri","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tz00hhakve3pzjfoak"},{"post_id":"cktk8o6tv00h5akvefji7akri","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6tz00hiakvehqy29hk7"},{"post_id":"cktk8o6tw00h8akveg72k4lri","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tz00hjakve6drfemmf"},{"post_id":"cktk8o6tw00h8akveg72k4lri","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tz00hkakvebh5jgt20"},{"post_id":"cktk8o6tx00hbakve9wq17mn4","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tz00hlakveeii8c7k3"},{"post_id":"cktk8o6tx00hbakve9wq17mn4","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6u000hmakve3o798w0s"}],"PostTag":[{"post_id":"cktk8o6o40009akvehm657ia2","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6o9000eakve99xw03t8"},{"post_id":"cktk8o6nw0001akve96rfdnwk","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6oa000hakvea9x224t8"},{"post_id":"cktk8o6o6000bakve2lmd5swc","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6oc000makvebz276qc1"},{"post_id":"cktk8o6o9000gakve5fnphajo","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6od000pakveh7lbd9ii"},{"post_id":"cktk8o6nz0003akve5a7o3s93","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6of000uakve4u9efz7l"},{"post_id":"cktk8o6od000oakveeb9m24v2","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6og000wakveaa9m4yhh"},{"post_id":"cktk8o6o30007akve3mey9pm8","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6oi0010akve1srnhcfv"},{"post_id":"cktk8o6og000vakve4p1e67aw","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6oj0012akve9jjncu8h"},{"post_id":"cktk8o6ob000jakve6uur2o8z","tag_id":"cktk8o6of000takvegfo3ddk2","_id":"cktk8o6ok0017akve1g8z5hin"},{"post_id":"cktk8o6oi0011akve2ujw438x","tag_id":"cktk8o6of000takvegfo3ddk2","_id":"cktk8o6ol001aakvebqgg0lpv"},{"post_id":"cktk8o6oe000rakve4wdsbjyw","tag_id":"cktk8o6of000takvegfo3ddk2","_id":"cktk8o6on001fakveg9jxfs9l"},{"post_id":"cktk8o6og000xakve4mebdy6p","tag_id":"cktk8o6ok0016akve4lvz1g74","_id":"cktk8o6oq001iakvecj0n78f9"},{"post_id":"cktk8o6oj0013akve45f3gq5i","tag_id":"cktk8o6om001eakvedgux10w5","_id":"cktk8o6ou001pakvecf8g1zee"},{"post_id":"cktk8o6ok0018akve39f5g6yx","tag_id":"cktk8o6om001eakvedgux10w5","_id":"cktk8o6ox001wakveb8dx0lqh"},{"post_id":"cktk8o6ox001xakvegmtv0lsp","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6p40023akvef1zw9vl8"},{"post_id":"cktk8o6ol001bakve1lqxghp8","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6p70027akve087n3kny"},{"post_id":"cktk8o6oo001gakve5kd5ad6i","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6p8002bakveej0jgqvd"},{"post_id":"cktk8o6oq001jakvebouthuqu","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6pa002iakveac4083j8"},{"post_id":"cktk8o6or001nakved1j01zfv","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6pd002pakve1emm0k2o"},{"post_id":"cktk8o6ou001qakve1mo0aal1","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6pf002wakve91ln64n6"},{"post_id":"cktk8o6ow001uakve6hqegt4b","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6ph0031akvedjml1ivm"},{"post_id":"cktk8o6pi0032akve2d4z141s","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pk0037akvedppuds1l"},{"post_id":"cktk8o6oy0020akvegdxh7rqn","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pk0039akve8gwz2hff"},{"post_id":"cktk8o6pj0035akvehrre9dbw","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pm003dakveef8l6vmf"},{"post_id":"cktk8o6pk0038akved9jw5nm3","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pn003fakvedgofapqd"},{"post_id":"cktk8o6p60024akvegkx6f24q","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6po003jakvebiypdp1z"},{"post_id":"cktk8o6p70028akve23cieqja","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pp003lakve329g61b7"},{"post_id":"cktk8o6p8002cakvedimceatv","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pq003qakve1gc28zby"},{"post_id":"cktk8o6p9002fakve7ps4flkp","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6ps003vakvefapxfrjf"},{"post_id":"cktk8o6pb002jakvefzhpc3tk","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pu0040akve29hpfqs1"},{"post_id":"cktk8o6pc002makvec2g3cfzf","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pw0045akvecucu5nz1"},{"post_id":"cktk8o6pd002qakve0ic15g6q","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6py004aakvefaifd5ng"},{"post_id":"cktk8o6pe002takvedtbz9vw9","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6q1004fakve3s3p41zm"},{"post_id":"cktk8o6pf002xakve102r3c73","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6q3004kakve7ubbfiuh"},{"post_id":"cktk8o6pg002zakve9wvvbwrm","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6q5004pakveh0fqac69"},{"post_id":"cktk8o6pl003bakve3evtamml","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6q9004uakvec8aifdz3"},{"post_id":"cktk8o6pm003eakve82ox37f6","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6qc004zakved5legib1"},{"post_id":"cktk8o6pn003hakvebel7gurk","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6qe0054akve8yn53scq"},{"post_id":"cktk8o6po003kakveajnw0fjv","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6qg0059akvefjzg81rv"},{"post_id":"cktk8o6pp003nakve3uzjgiks","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6qj005eakvecpt56v8v"},{"post_id":"cktk8o6pq003pakveexb0ak5a","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6ql005kakveceycaoh2"},{"post_id":"cktk8o6pr003sakve2exe8imp","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qn005rakve0j7v0rbn"},{"post_id":"cktk8o6ps003uakve66vo01gc","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qp005yakve3bnpd8am"},{"post_id":"cktk8o6pt003xakve53qs5vxp","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qr0065akvebg8p8jkw"},{"post_id":"cktk8o6pu003zakve3wlwdsem","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qu006cakve2r3e6hio"},{"post_id":"cktk8o6pv0042akve76zr0o90","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qw006jakve63ipa2y6"},{"post_id":"cktk8o6pw0044akveh5zqdjte","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qy006qakveh4l0fc9w"},{"post_id":"cktk8o6px0047akve8seydg2u","tag_id":"cktk8o6qx006nakvefl9gfr5d","_id":"cktk8o6r20073akvecg2sdqkl"},{"post_id":"cktk8o6px0047akve8seydg2u","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6r30077akve6izva8aw"},{"post_id":"cktk8o6py0049akve15r5eo74","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6r5007bakve6bi5b95j"},{"post_id":"cktk8o6py004cakve0g6zci7t","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6r6007iakveabawgygy"},{"post_id":"cktk8o6q0004eakve6dzd49z7","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6r8007pakve0hricnbi"},{"post_id":"cktk8o6q1004hakve7ci71f3m","tag_id":"cktk8o6qx006nakvefl9gfr5d","_id":"cktk8o6rd0082akveebnh4zfb"},{"post_id":"cktk8o6q1004hakve7ci71f3m","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6re0085akvegku0657k"},{"post_id":"cktk8o6q2004jakvedojmd1vx","tag_id":"cktk8o6qx006nakvefl9gfr5d","_id":"cktk8o6rh008gakve0c5u6vme"},{"post_id":"cktk8o6q2004jakvedojmd1vx","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6ri008jakve21jx5ws3"},{"post_id":"cktk8o6q4004makve8gio9l5r","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6rk008pakve14ohgsxh"},{"post_id":"cktk8o6q5004oakve8g0k1g9x","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6rl008uakve60pnc5xl"},{"post_id":"cktk8o6q6004rakve7m1j1plt","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6rm008zakve8zfec08u"},{"post_id":"cktk8o6q7004takvecc6ph7mb","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6ro0094akve58ztg33g"},{"post_id":"cktk8o6q9004wakve8yyz5ix5","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6rp0099akve8x889ecg"},{"post_id":"cktk8o6qb004yakve5qk6a50r","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6rp009eakvegwlkgex4"},{"post_id":"cktk8o6qc0051akve1nql6qt7","tag_id":"cktk8o6rp009cakvecleqe5lf","_id":"cktk8o6rq009jakve54gh4g6m"},{"post_id":"cktk8o6qd0053akve87tvdadq","tag_id":"cktk8o6rp009cakvecleqe5lf","_id":"cktk8o6rr009oakvefdz6362h"},{"post_id":"cktk8o6qe0056akvea9ks9ha1","tag_id":"cktk8o6rp009cakvecleqe5lf","_id":"cktk8o6rv009takveh4eo6q7z"},{"post_id":"cktk8o6qf0058akve50qfda62","tag_id":"cktk8o6ru009rakve2ntgf97s","_id":"cktk8o6ry009yakvehat0a2nv"},{"post_id":"cktk8o6qg005aakveh3vd1y50","tag_id":"cktk8o6rp009cakvecleqe5lf","_id":"cktk8o6rz00a3akve8za18n5w"},{"post_id":"cktk8o6qj005dakve37dy5lne","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s000a8akve2n5j4l3s"},{"post_id":"cktk8o6qj005fakvegpvj86dz","tag_id":"cktk8o6s000a7akve1f33c9rq","_id":"cktk8o6s100aeakve87e8emc9"},{"post_id":"cktk8o6ql005jakve8xo8cwcv","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s200ajakvec4c44dz8"},{"post_id":"cktk8o6ql005makve7a1dc654","tag_id":"cktk8o6ru009rakve2ntgf97s","_id":"cktk8o6s300anakvee7ny0dz1"},{"post_id":"cktk8o6qn005qakve26rcd19s","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s400asakve19n9ck5b"},{"post_id":"cktk8o6qn005takveawfj22pb","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s500axakve166lfsxn"},{"post_id":"cktk8o6qp005wakvedimp04vg","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s700b2akveadv5bgnz"},{"post_id":"cktk8o6qq005zakve2q775ly3","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s800b7akve18uee020"},{"post_id":"cktk8o6qr0063akvedrb617kz","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s900bcakve1a06hfrq"},{"post_id":"cktk8o6qs0066akve3ppb6e1q","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sa00bhakveasvl2yw4"},{"post_id":"cktk8o6qt006aakvealgt52ey","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sb00bmakve0tdi89f4"},{"post_id":"cktk8o6qu006dakve8q0q3khe","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sc00brakvecbru8jgt"},{"post_id":"cktk8o6qv006hakve2qm4crpn","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sd00bwakve73vw8w1m"},{"post_id":"cktk8o6qw006kakvec7h0d155","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6se00c1akve7o6o2tdi"},{"post_id":"cktk8o6qx006oakveh6kj9q64","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sg00c6akvehk2i67pg"},{"post_id":"cktk8o6qy006rakve1z853ljh","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sh00cbakve07uyfw5t"},{"post_id":"cktk8o6qz006uakve9nqxfhzr","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6si00cgakve03bb0pf8"},{"post_id":"cktk8o6r0006xakve9680bvvn","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sj00clakve62er0udh"},{"post_id":"cktk8o6r10070akvedu2zcjwq","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sk00cqakve7z2wacsk"},{"post_id":"cktk8o6r20074akve5rp09eoj","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sl00cvakvecklogdi3"},{"post_id":"cktk8o6r30078akvehflj72w7","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sm00d0akvedeuv4pop"},{"post_id":"cktk8o6r5007cakve2yiob0j3","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6so00d5akvegyx8fk5p"},{"post_id":"cktk8o6r5007fakve7bba4t62","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sp00daakvedd9x8x0f"},{"post_id":"cktk8o6r7007lakve2vjjaimr","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sq00dfakve4oz690bc"},{"post_id":"cktk8o6r8007qakvebtub8ep8","tag_id":"cktk8o6sq00ddakve9lizf5ee","_id":"cktk8o6sr00dkakve5j4i5unw"},{"post_id":"cktk8o6r9007sakvea9jb34oj","tag_id":"cktk8o6sq00diakve7a9v2s38","_id":"cktk8o6sr00dpakve61vf3t37"},{"post_id":"cktk8o6rb007wakve7c6t6uot","tag_id":"cktk8o6sq00ddakve9lizf5ee","_id":"cktk8o6ss00duakve59ev8uss"},{"post_id":"cktk8o6rc007yakve8853hjrp","tag_id":"cktk8o6ss00dtakvedmobezfr","_id":"cktk8o6st00dzakve4dbn9di2"},{"post_id":"cktk8o6rd0083akve82iv1jsm","tag_id":"cktk8o6ss00dtakvedmobezfr","_id":"cktk8o6su00e4akveazragvqw"},{"post_id":"cktk8o6re0086akve1a6d4ffa","tag_id":"cktk8o6ss00dtakvedmobezfr","_id":"cktk8o6sz00e9akve3hzsgsos"},{"post_id":"cktk8o6rf008aakve5eeddxx1","tag_id":"cktk8o6sy00e8akveh7cobd7s","_id":"cktk8o6t400efakve2f24927f"},{"post_id":"cktk8o6rg008cakve0mvn1u4x","tag_id":"cktk8o6t400edakveb0x0b1yt","_id":"cktk8o6t500elakvebsk5fpor"},{"post_id":"cktk8o6rh008hakveh2yhheff","tag_id":"cktk8o6t500eiakveb5k3115m","_id":"cktk8o6t700eqakve6f1x5d25"},{"post_id":"cktk8o6ri008kakve8zmh4jwv","tag_id":"cktk8o6t500eiakveb5k3115m","_id":"cktk8o6t700evakveaz2f2l98"},{"post_id":"cktk8o6rj008oakvef3va6zbp","tag_id":"cktk8o6t700esakve0gfc2fsv","_id":"cktk8o6t800ezakve7gso83vf"},{"post_id":"cktk8o6tt00h0akvehg5w1fi3","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6tu00h2akve6fyv32zq"},{"post_id":"cktk8o6tu00h1akve7d054dj8","tag_id":"cktk8o6of000takvegfo3ddk2","_id":"cktk8o6tv00h4akve5l835noq"},{"post_id":"cktk8o6tu00h3akve8scabvi5","tag_id":"cktk8o6ok0016akve4lvz1g74","_id":"cktk8o6tw00h7akve1g61fxqr"},{"post_id":"cktk8o6tv00h5akvefji7akri","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6tx00haakve3p221u83"},{"post_id":"cktk8o6tw00h8akveg72k4lri","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6tz00hdakvefkef78rp"},{"post_id":"cktk8o6tx00hbakve9wq17mn4","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6tz00hfakve4tt1em2l"}],"Tag":[{"name":"51单片机","_id":"cktk8o6o20005akve3o8naofq"},{"name":"Hexo","_id":"cktk8o6of000takvegfo3ddk2"},{"name":"Java面试","_id":"cktk8o6ok0016akve4lvz1g74"},{"name":"Maven","_id":"cktk8o6om001eakvedgux10w5"},{"name":"MySQL","_id":"cktk8o6ow001takveefgsbxmx"},{"name":"spring","_id":"cktk8o6ph0030akve7x5udh45"},{"name":"Mybatis","_id":"cktk8o6q5004nakve0wscabgm"},{"name":"esp8266","_id":"cktk8o6qk005hakve2n3l2y1j"},{"name":"物联网","_id":"cktk8o6qx006nakvefl9gfr5d"},{"name":"springMVC","_id":"cktk8o6rg008eakve4ce71gs4"},{"name":"Servlet","_id":"cktk8o6rp009cakvecleqe5lf"},{"name":"Tomcat","_id":"cktk8o6ru009rakve2ntgf97s"},{"name":"javaSE","_id":"cktk8o6ry00a1akve04uf69xm"},{"name":"Http","_id":"cktk8o6s000a7akve1f33c9rq"},{"name":"win10","_id":"cktk8o6sq00ddakve9lizf5ee"},{"name":"MDK-keil5","_id":"cktk8o6sq00diakve7a9v2s38"},{"name":"stm32","_id":"cktk8o6ss00dtakvedmobezfr"},{"name":"图床","_id":"cktk8o6sy00e8akveh7cobd7s"},{"name":"矢量","_id":"cktk8o6t400edakveb0x0b1yt"},{"name":"网络通信","_id":"cktk8o6t500eiakveb5k3115m"},{"name":"网络安全","_id":"cktk8o6t700esakve0gfc2fsv"}]}} \ No newline at end of file +{"meta":{"version":1,"warehouse":"4.0.0"},"models":{"Asset":[{"_id":"themes/butterfly/source/css/copyright.css","path":"css/copyright.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/background.css","path":"css/background.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/custom.css","path":"css/custom.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/icon.css","path":"css/icon.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/index.min.css","path":"css/index.min.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/mouse.css","path":"css/mouse.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/index.styl","path":"css/index.styl","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/side.css","path":"css/side.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/css/var.styl","path":"css/var.styl","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/JetBrainsMono-Medium.woff2","path":"fonts/JetBrainsMono-Medium.woff2","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/ZhuZiAWan.woff2","path":"fonts/ZhuZiAWan.woff2","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/ajLangMan.ttf","path":"fonts/ajLangMan.ttf","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/loving.ttf","path":"fonts/loving.ttf","modified":0,"renderable":1},{"_id":"themes/butterfly/source/fonts/shishangblack.ttf","path":"fonts/shishangblack.ttf","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/01.jpg","path":"img/01.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/02.jpg","path":"img/02.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/404.jpg","path":"img/404.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/34.jpg","path":"img/34.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/articles.png","path":"img/articles.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/algolia.svg","path":"img/algolia.svg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/articles2.png","path":"img/articles2.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/avatar1.jpg","path":"img/avatar1.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/avatar2.jpg","path":"img/avatar2.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/banner.png","path":"img/banner.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/blissed01.jpg","path":"img/blissed01.jpg","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/categories.png","path":"img/categories.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/favicon.png","path":"img/favicon.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/friend_404.gif","path":"img/friend_404.gif","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/huiji.png","path":"img/huiji.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/mybatis.png","path":"img/mybatis.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/mybatis1.png","path":"img/mybatis1.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/pcbimg.png","path":"img/pcbimg.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/pcbimg2.png","path":"img/pcbimg2.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/plane.png","path":"img/plane.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/img/plane1.png","path":"img/plane1.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/main.js","path":"js/main.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/tw_cn.js","path":"js/tw_cn.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/utils.js","path":"js/utils.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/LICENSE","path":"live2d-widget/LICENSE","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/README.md","path":"live2d-widget/README.md","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/autoload.js","path":"live2d-widget/autoload.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/live2d.min.js","path":"live2d-widget/live2d.min.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.js","path":"live2d-widget/waifu-tips.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.json","path":"live2d-widget/waifu-tips.json","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/waifu.css","path":"live2d-widget/waifu.css","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/AppStarting.ani","path":"mouse/AppStarting.ani","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Arrow.cur","path":"mouse/Arrow.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Cross.cur","path":"mouse/Cross.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Hand.cur","path":"mouse/Hand.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Handwriting.cur","path":"mouse/Handwriting.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Help.cur","path":"mouse/Help.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/IBeam.cur","path":"mouse/IBeam.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/NO.cur","path":"mouse/NO.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeAll.cur","path":"mouse/SizeAll.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeNESW.cur","path":"mouse/SizeNESW.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeNWSE.cur","path":"mouse/SizeNWSE.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeNS.cur","path":"mouse/SizeNS.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/SizeWE.cur","path":"mouse/SizeWE.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/UpArrow.cur","path":"mouse/UpArrow.cur","modified":0,"renderable":1},{"_id":"themes/butterfly/source/mouse/Wait.ani","path":"mouse/Wait.ani","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/search/algolia.js","path":"js/search/algolia.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/js/search/local-search.js","path":"js/search/local-search.js","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-1.png","path":"live2d-widget/assets/screenshot-1.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-2.png","path":"live2d-widget/assets/screenshot-2.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-3.png","path":"live2d-widget/assets/screenshot-3.png","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/demo/demo.html","path":"live2d-widget/demo/demo.html","modified":0,"renderable":1},{"_id":"themes/butterfly/source/live2d-widget/demo/login.html","path":"live2d-widget/demo/login.html","modified":0,"renderable":1}],"Cache":[{"_id":"source/link/index.md","hash":"b523cebc9646f9e71dced10c15286446b5ec05f9","modified":1629177172461},{"_id":"source/_data/link.yml","hash":"ac42ffbb8934c77eaaa451aaaee0e997edfbef39","modified":1630841898931},{"_id":"source/about/index.md","hash":"2cbe86fa0f91f1ec4e9e9f50d2622ba6fb334c4b","modified":1629470228214},{"_id":"source/categories/index.md","hash":"85594ad9ae83a45a6fd1f1bb812d1c348f0e0d04","modified":1629199613838},{"_id":"source/notes/index.md","hash":"cd1eca6299ad86555ea010220c23a582e1d1d689","modified":1630456117545},{"_id":"source/poems/index.md","hash":"4a8f9bd31f5b9f30a41e5c168294ff8006cfe2c0","modified":1630495317769},{"_id":"source/music/index.md","hash":"18c94b16f0d06e13daf5574fba544e4beee1888e","modified":1631436778221},{"_id":"source/tags/index.md","hash":"7d44ddaac9bce3ef3d0747699ad557271b21e2e2","modified":1629177106636},{"_id":"source/web-links/index.md","hash":"ed38c45ed78d5406a00f6c1c76fe9f98b03e6403","modified":1630461912181},{"_id":"source/_posts/51单片机/51单片机学习笔记(一).md","hash":"baa93866bf0443a9487ceadf241ea3e6191c644a","modified":1631277953371},{"_id":"source/_posts/51单片机/51单片机学习笔记(七).md","hash":"5ab18e79564dd583a3ff8a2bf3d9003ff862e599","modified":1631277926554},{"_id":"source/_posts/51单片机/51单片机学习笔记(三).md","hash":"b77cb8e4e58ba7affe617b7147b698d826fc3b2d","modified":1631277931265},{"_id":"source/_posts/51单片机/51单片机学习笔记(九).md","hash":"0fb139f3222ec409ca81bb298615c252bb79f6c4","modified":1631277912950},{"_id":"source/_posts/51单片机/51单片机学习笔记(二).md","hash":"c2df9e5ec32fba24ee910a0d2c234e5deafe1f76","modified":1631277906012},{"_id":"source/_posts/51单片机/51单片机学习笔记(五).md","hash":"6429fd26f9e6ab42720347db1992a5fbc97d2f5f","modified":1631277942618},{"_id":"source/manifest.json","hash":"c06ff8a12fb81cfe47befc9d3941aacfae49ddff","modified":1631611477371},{"_id":"source/_posts/51单片机/51单片机学习笔记(八).md","hash":"b5b3eac1a371a6578b27d7852d2fa707dac5c42d","modified":1631277895652},{"_id":"source/_posts/Hexo/Hexo添加百度统计分析.md","hash":"ad110dfc1e83663d1c3906d89005cdb5b2b192cf","modified":1631806274302},{"_id":"source/_posts/51单片机/51单片机学习笔记(六).md","hash":"bed0d77a7c301947aa63f142220c27b865207a18","modified":1631277919484},{"_id":"source/_posts/51单片机/51单片机学习笔记(四).md","hash":"121d5f350d59cbce348b1d505f5a39a62036f1e4","modified":1631277936194},{"_id":"source/_posts/Hexo/butterfly主题配置.md","hash":"642f98cc33649a81685e660a687364d9f0e4d338","modified":1631806136062},{"_id":"source/_posts/Java面试/Java垃圾回收算法.md","hash":"c3b1129a2cbc588037e603bf4d9fba0116158a03","modified":1631347297796},{"_id":"source/_posts/Hexo/github Actions自动部署.md","hash":"ffb2b229f96e063815bcb43494cd15ccac5d9a5b","modified":1631806189622},{"_id":"source/_posts/Maven/1-maven基本概念.md","hash":"b721b4c3648699ce80e70b838843d2413027b96a","modified":1631242769812},{"_id":"source/_posts/Hexo/标签外挂.md","hash":"6187523670c82eb272c8f40fdee46b4467e3a6c1","modified":1631806122688},{"_id":"source/_posts/Maven/3-maven+IDEA创建webapp.md","hash":"24ef7a6e34687f49bf679e80d4e3cb75b8b98f2d","modified":1631242813294},{"_id":"source/_posts/Java面试/java面试题.md","hash":"47f820465287ffb7f9cf553b0be66aacf31c5d43","modified":1630720868705},{"_id":"source/_posts/MySQL/4-MySQL事务.md","hash":"9762f26985acb72368eb4a7ea8b6c6eba05c5faf","modified":1631106363208},{"_id":"source/_posts/MySQL/1-数据库认识.md","hash":"69ef32bfb52656fc614d7bb34317c45bed43af3a","modified":1631242870897},{"_id":"source/_posts/MySQL/2-MySQL基础.md","hash":"2b07760222f7dc7d425384768eda92c763fa4ac7","modified":1631106379369},{"_id":"source/_posts/MySQL/5-索引.md","hash":"54678b3e8b7c0424178ba9bc356dbed1b535187e","modified":1631106357536},{"_id":"source/_posts/MySQL/3-数据管理.md","hash":"526b0eec8b3c745a9543b8d5802cd52c4e9bda2b","modified":1631242915065},{"_id":"source/_posts/Spring/1-spring框架介绍.md","hash":"4c12fd3d702fbdde0e3c78008d84883b134df0b1","modified":1631243841676},{"_id":"source/_posts/MySQL/6-权限-备份.md","hash":"47af43803556a42b669ca47bba616a7853203e1d","modified":1631106351506},{"_id":"source/_posts/Spring/10-注解实现自动装配.md","hash":"696a8643c333620c9eafd1f3dc9b6ac54ce84639","modified":1631243823959},{"_id":"source/_posts/Spring/12-静态代理.md","hash":"ce877429ec005160d28d09a33bf1f16e364aa935","modified":1631243812129},{"_id":"source/_posts/MySQL/7-JDBC.md","hash":"305788488fc48dbb74c73517ba15c2eed0eec6e7","modified":1631242990248},{"_id":"source/_posts/Spring/11-使用注解开发.md","hash":"8adf385ed6593347df06ec5dd923c6c7f1d345bd","modified":1631243818357},{"_id":"source/_posts/Spring/2-SpringIOC.md","hash":"e3a00f743c9a3dd346761d2a800c5998de95d8b8","modified":1631243889108},{"_id":"source/_posts/Spring/14-springAOP.md","hash":"fe9e27a79ea54944c5895bfdfce143c3186b47f9","modified":1631243778937},{"_id":"source/_posts/Spring/3-ioc控制反转.md","hash":"5d6dc86b0671061d8a92a8bc50e8632c279dfbe5","modified":1631243873109},{"_id":"source/_posts/Spring/13-动态代理.md","hash":"47b762cb652146dd068fe76f4080ce9decaf8cff","modified":1631243800037},{"_id":"source/_posts/Spring/7-spring容器注入.md","hash":"1e72586920062aeb3c7c39258470b53555578d47","modified":1631243912070},{"_id":"source/_posts/Spring/5-ioc创建对象方式.md","hash":"724e4210c04ef41ce123f7026151aec89a4e66dd","modified":1631243900605},{"_id":"source/_posts/Spring/4-HelloSpring程序.md","hash":"bbe75d0e4aa6cb9ed065b771be37c69392227620","modified":1631243881840},{"_id":"source/_posts/Spring/6-spring配置说明.md","hash":"4c06ce7bab72f94b6e46774f1773997b4d76f090","modified":1631243904710},{"_id":"source/_posts/Spring/8-命名空间和作用域.md","hash":"9401035baf7cd2bd15f608b48730e70ed02a1640","modified":1631243833986},{"_id":"source/_posts/Mybatis/mybatis关联查询(四).md","hash":"b6c78531032c3896d7c1f20086e27416f4c7731e","modified":1630314135636},{"_id":"source/_posts/Spring/9-bean自动装配.md","hash":"b7608f63c78c92f5df4342d582c77c22ca3b1980","modified":1631243829012},{"_id":"source/_posts/Mybatis/mybatis动态sql(七).md","hash":"5b5cccaf4b436fa4516447fb86afe1b34eb638c1","modified":1630248324576},{"_id":"source/_posts/Mybatis/mybatis延迟加载(六).md","hash":"6c245ee1328b1492dfcbf53448f4b7d400f0fdd6","modified":1630331393564},{"_id":"source/_posts/Mybatis/mybatis的Mapper详解(三).md","hash":"9d1cf80be6c69ef5f3f6fe840940d50453e8e09c","modified":1630248305560},{"_id":"source/_posts/Mybatis/mybatis简单使用(二).md","hash":"a5588151a36c8144206562e2a711af39ac9ba893","modified":1630248299444},{"_id":"source/_posts/Mybatis/mybatis简单介绍与使用(一).md","hash":"2a1b3741be4325b72ce72b46c99943c7988e564d","modified":1630248286760},{"_id":"source/_posts/Mybatis/mybatis逆向工程(五).md","hash":"80fadcf88c09c5e70a0e43a7883c4a905176efaf","modified":1630248315143},{"_id":"source/_posts/esp8266/0-Arduino配置esp8266开发环境.md","hash":"1ae7dabb617ed7af563ab4fc971fa2ff3346bc7b","modified":1631346932130},{"_id":"source/_posts/esp8266/1-接入点模式-无线终端模式.md","hash":"d6b50a08f00138a630722ab9cefa7804e28bdac6","modified":1631243986998},{"_id":"source/_posts/esp8266/2-网络服务器.md","hash":"a145f1fbefcc624a4080db65fb41cffef88f575f","modified":1631243997136},{"_id":"source/_posts/esp8266/3-闪存文件系统.md","hash":"665075ac149ce26c7ae45cc1632d5b5247155fb7","modified":1631346924239},{"_id":"source/_posts/esp8266/5-Stream数据流.md","hash":"2059838c8bbbd5ae5338438f4e59189252a82a5b","modified":1631346878317},{"_id":"source/_posts/esp8266/4-NodeMCU作为客户端使用.md","hash":"32dd90d0f0a098bf778d9df7c338094137a509b3","modified":1631346862484},{"_id":"source/_posts/esp8266/8-OTA上传代码.md","hash":"c135b08812c2e0646f859d788d9bf45aa9250b91","modified":1631346901650},{"_id":"source/_posts/esp8266/MQTT篇(一)-认识MQTT与基本操作.md","hash":"92e8dafacbff0a5f3c229d36984db24054b6cbcc","modified":1631346808088},{"_id":"source/_posts/esp8266/7-多任务执行.md","hash":"fe76210a836b07eebc72e106657bd31e9c1211f0","modified":1631346890150},{"_id":"source/_posts/esp8266/6-WiFi配网.md","hash":"d422ebb4c9ded7c59d0dd7511b19e3d88bcae27a","modified":1631346886084},{"_id":"source/_posts/esp8266/MQTT篇(三)-主题.md","hash":"a058bd9738090459b257442d7b58abb338ed4f72","modified":1630248886346},{"_id":"source/_posts/esp8266/MQTT篇(二)-发布、订阅、取消订阅.md","hash":"d19e95d87777a83680af4eabe4b46f9139907966","modified":1630248882648},{"_id":"source/_posts/SpringMVC/springMVC(二)HelloMVC.md","hash":"f8e912976546f72f47b177bf009c4047c35d9f91","modified":1630838038666},{"_id":"source/_posts/SpringMVC/springMVC(三)使用注解开发.md","hash":"7e2d4406b2a227668dec925352eaf45ec626b550","modified":1630838043284},{"_id":"source/_posts/SpringMVC/springMVC(一)快速入门.md","hash":"47df9392752049d5b2e0a1d89f39784a70694268","modified":1630838027417},{"_id":"source/_posts/SpringMVC/springMVC(四)Controller与restful.md","hash":"0c393bcd4bd730b37abdc8a0bd284732cc479384","modified":1630838047415},{"_id":"source/_posts/SpringMVC/springMVC(六)数据处理.md","hash":"ecfa53b64252b8a777f8a12b80535836366ebb4d","modified":1630838056597},{"_id":"source/_posts/javaEE/3-ServletRequest对象.md","hash":"12b2f1398cbf09df5b3d75cf4e6635367791655c","modified":1631243407722},{"_id":"source/_posts/SpringMVC/springMVC(五)结果跳转方式.md","hash":"4ae1e8827eafb617ef1747747666088b1c643675","modified":1630838051953},{"_id":"source/_posts/javaEE/2-Servlet生命周期.md","hash":"48a296346a3c6e86079a15682d8d935f52f0fb07","modified":1631243355138},{"_id":"source/_posts/javaEE/1-Servlet.md","hash":"c7270b3dccaa9fbac4bb7a880da86fd54eedb857","modified":1631243338332},{"_id":"source/_posts/javaEE/IDEA配置Tomcat.md","hash":"57335e93f1c10a450b18f759c99607235c5fdf1d","modified":1631243461852},{"_id":"source/_posts/javaEE/http.md","hash":"cd4021433ae1ac20e4dd6f1f9a40316f0c69b01d","modified":1630677215404},{"_id":"source/_posts/javaEE/4-请求转发.md","hash":"51c6485851dfc005dcc187e62a5c18147059b6ef","modified":1631243421610},{"_id":"source/_posts/javaEE/Tomcat基础.md","hash":"de9787f7bf7a2f41fcc6a2f4df6fef969e73d1f0","modified":1630671686646},{"_id":"source/_posts/javaSE/10-装箱与拆箱.md","hash":"b70216ab8145f5f31a461c09e0981606264097f2","modified":1630251080436},{"_id":"source/_posts/javaSE/1-类与对象.md","hash":"cd815d389512cc1ea6ee5596c3abccea4160df9b","modified":1631243135295},{"_id":"source/_posts/javaSE/11-数字与字符串相互转换.md","hash":"4f1b133b565eae5977800de91d6eb8266621f826","modified":1630251084118},{"_id":"source/_posts/javaSE/12-字符串操作.md","hash":"658bf9a88b0d6dd97e82c1e11ae37a9b9f84caae","modified":1630251087482},{"_id":"source/_posts/javaSE/14-接口与继承.md","hash":"4198da5d1d74a47f2fa49b09141631318db96c23","modified":1630251095517},{"_id":"source/_posts/javaSE/13-日期.md","hash":"ae9aaed18184ef39f36ef26152111df220172c65","modified":1630251091919},{"_id":"source/_posts/javaSE/15-异常处理.md","hash":"2ec30d857c147fdacc21419556f48f9e78fa671c","modified":1630251098885},{"_id":"source/_posts/javaSE/16-文件IO.md","hash":"bcd6604db1fa6f0c96b03a07b2b9bdfbbcc28d9f","modified":1630251102266},{"_id":"source/_posts/javaSE/17-容器.md","hash":"3cb845c57d11ceb506bf2eb3c321d8a7a56abb07","modified":1630251116883},{"_id":"source/_posts/javaSE/17-集合框架.md","hash":"9d1e5fd4a725c71ad667d51d2dc638152be6c3a6","modified":1630251105529},{"_id":"source/_posts/javaSE/18-泛型.md","hash":"286cc161aff3cf7577cf60d64eeaef6aa861eea7","modified":1630251121869},{"_id":"source/_posts/javaSE/2.2-访问修饰符.md","hash":"b01d48e5e16a4016638f2ae7f977152b30fc6ef7","modified":1630251038950},{"_id":"source/_posts/javaSE/19-多线程系列.md","hash":"74a44f6b72f52971feefc14012f22f1eed5dd49f","modified":1630251125689},{"_id":"source/_posts/javaSE/2.1-类和对象的属性初始化.md","hash":"64bb8acbe190e10d7a13e568920368ff3bd4c94b","modified":1630251034836},{"_id":"source/_posts/javaSE/21-注解.md","hash":"948bf85840b582a6d5333bf6dd56a010e5324c49","modified":1630251134568},{"_id":"source/_posts/javaSE/3.1-数据类型.md","hash":"3a380b105bf644c6905b3d32a4382dd495c26f0b","modified":1630251043790},{"_id":"source/_posts/javaSE/3.2-数组.md","hash":"2cf0a2ca05c216f0f83842ce9654d81a73d86321","modified":1630251047232},{"_id":"source/_posts/javaSE/20-反射机制.md","hash":"115db83be1b9e6a83c1b005757dc670a5a49b8d2","modified":1630251130453},{"_id":"source/_posts/javaSE/4-变量.md","hash":"abc5bd6ca9258e7b14570e03c66d1520cb59d483","modified":1630251050603},{"_id":"source/_posts/javaSE/5-操作符.md","hash":"1ec84eddc3febdebc9540dd7c0718361ec2ec8a3","modified":1630251053821},{"_id":"source/_posts/javaSE/6-输入语句.md","hash":"e2995606574b6e504d1202ff261537f53f95718d","modified":1630251057135},{"_id":"source/_posts/javaSE/7-条件语句.md","hash":"e43786948b7a87e74960b8459b30eea0bfd81df5","modified":1630312166817},{"_id":"source/_posts/javaSE/8-循环语句.md","hash":"7797c7d3f5730b6507c9e83b2b15720320075b00","modified":1630251071601},{"_id":"source/_posts/win10/win10任务栏透明.md","hash":"daa7cba90b6693dddc8f378234a96d31d1a90c28","modified":1631024957913},{"_id":"source/_posts/javaSE/9-package包.md","hash":"e71ddfcdc911f3455aa514c0458974a2a9c84bb7","modified":1630251074685},{"_id":"source/_posts/win10/win10创建系统还原节点.md","hash":"d88958fa074d91459ad9df888b6dc46ab0fbe3ab","modified":1631112351921},{"_id":"source/_posts/win10/win10主题美化.md","hash":"834b1c6baa1802570ca526f151a8ab8dd4e27f51","modified":1630834352290},{"_id":"source/_posts/stm32/MDK_Keil卸载已经安装的pack.md","hash":"50804d67534ad7f9b9214ff801d2a1bc3579b271","modified":1631278107623},{"_id":"source/_posts/stm32/stm32标准外设库介绍.md","hash":"49b69166a379f02c46ca6d5bce694e707600e515","modified":1631417937863},{"_id":"source/_posts/stm32/keil新建库函数版本的project.md","hash":"bc5ec136879bdfbd73eeadc5d26fd83502fcef36","modified":1631285270686},{"_id":"source/_posts/stm32/stm32点亮led灯程序.md","hash":"47e58ef7caa184c1e68294bbfd6c3fc02cfdd9eb","modified":1631411594761},{"_id":"source/_posts/杂七杂八/gitee图床配置.md","hash":"71a1ffcb7d8384734563cff8f2acdbcf9c99ed04","modified":1631536238795},{"_id":"source/_posts/网络安全/古典加密算法.md","hash":"c07a4af38074ebd217f0926bfc94ff12d06e020d","modified":1631802714376},{"_id":"source/_posts/网络通信/计算机网络的产生.md","hash":"a8774c3ea7a7502a6f19109ac7a9c46adce82e81","modified":1631498410634},{"_id":"source/_posts/电磁场/矢量场.md","hash":"071499e078c59f02d2813c38988c15dc6f53ad34","modified":1631092459444},{"_id":"source/_posts/网络通信/网络协议.md","hash":"821f48c6c4d29fd76d7ce17b784bd6e63b7d492c","modified":1631498054362},{"_id":"themes/butterfly/README_CN.md","hash":"459d6f3200863021bee1fe72a719aef236fb4090","modified":1627578032000},{"_id":"themes/butterfly/LICENSE","hash":"1128f8f91104ba9ef98d37eea6523a888dcfa5de","modified":1627578032000},{"_id":"themes/butterfly/package.json","hash":"c18b118422237b964063874a099945b79573feca","modified":1627578032000},{"_id":"themes/butterfly/languages/en.yml","hash":"cd333235ff1648a6bf58dfafc81f2c57672a15a5","modified":1627578032000},{"_id":"themes/butterfly/languages/default.yml","hash":"7ca673fb629ea74f5ba5e75b4f0f95248cfb5090","modified":1627578032000},{"_id":"themes/butterfly/languages/zh-CN.yml","hash":"741e522b2387f94764a73844e7b084cc7e927c54","modified":1627578032000},{"_id":"themes/butterfly/_config.yml.bak","hash":"8609065adff452353410b14cced654a0a7430914","modified":1629765958049},{"_id":"themes/butterfly/languages/zh-TW.yml","hash":"79a50c40d9f5463f1fa42aa870ac6b8b84540412","modified":1627578032000},{"_id":"themes/butterfly/layout/archive.pug","hash":"bd62286afb64a51c97e800c5945620d51605d5fa","modified":1627578032000},{"_id":"themes/butterfly/layout/category.pug","hash":"60c1b795b6e227b5dd81963b51d29d1b81d0bf49","modified":1627578032000},{"_id":"themes/butterfly/layout/index.pug","hash":"201b6f40da9a60ef76e4f51c5dd1440114dcb303","modified":1630593644328},{"_id":"themes/butterfly/layout/page.pug","hash":"82aa988527a11835e7ac86ce4f23b8cd20014dfa","modified":1627578032000},{"_id":"themes/butterfly/layout/tag.pug","hash":"0440f42569df2676273c026a92384fa7729bc4e9","modified":1627578032000},{"_id":"themes/butterfly/README.md","hash":"cedd13fcd8c75a68742265dd8eced4087e940ffd","modified":1627578032000},{"_id":"themes/butterfly/scripts/year.js","hash":"a72100c58fc177fc1ec123ece67bb216a32911b7","modified":1629471168674},{"_id":"themes/butterfly/.github/stale.yml","hash":"05a55a87fa7f122c59683e41c8b2e37e79f688f0","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/404.pug","hash":"7d378e328a53cc99d5acc9682dce53f5eb61537d","modified":1627578032000},{"_id":"themes/butterfly/.github/ISSUE_TEMPLATE/feature_request.md","hash":"f6867a2f0417fe89a0f2008730ee19dd38422021","modified":1627578032000},{"_id":"themes/butterfly/layout/post.pug","hash":"8d398c8925182699d9f2b9f1b727f06228488312","modified":1627578032000},{"_id":"themes/butterfly/.github/ISSUE_TEMPLATE/custom.md","hash":"eff495eb1584cf4586e33c76e8b2fa6a469a179b","modified":1627578032000},{"_id":"themes/butterfly/.github/ISSUE_TEMPLATE/bug_report.md","hash":"476802922b774b679225102ac30a9d9183394701","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/footer.pug","hash":"02390a5b6ae1f57497b22ba2e6be9f13cfb7acac","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/additional-js.pug","hash":"4156224c47bfc2482281ac4e4df701c30476ff00","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head.pug","hash":"1377952022ee0a9eaa7a2fd1098f1571efc468d9","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/layout.pug","hash":"6f2608c4d93d3d10ae6b2cd7f8918f303f024321","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/sidebar.pug","hash":"8dafc2dcd8c33f70a546fee443f0b6d80b3cd243","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/pagination.pug","hash":"0b80f04950bd0fe5e6c4e7b7559adf4d0ce28436","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/rightside.pug","hash":"2d0453adf92a3fd3466cf0793f14685d17b8b51d","modified":1627578032000},{"_id":"themes/butterfly/scripts/events/init.js","hash":"018aa446265fe627301b1d53d7cba4f4ff1960ac","modified":1627578032000},{"_id":"themes/butterfly/scripts/events/404.js","hash":"83cd7f73225ccad123afbd526ce1834eb1eb6a6d","modified":1627578032000},{"_id":"themes/butterfly/scripts/filters/post_lazyload.js","hash":"4cc2d517195c8779471d326ada09f9371cbad4dd","modified":1627578032000},{"_id":"themes/butterfly/scripts/events/welcome.js","hash":"d575137c8779e50422c2416f4d0832fdea346ee6","modified":1627578032000},{"_id":"themes/butterfly/scripts/filters/random_cover.js","hash":"9821872007cf57efae4b728dc575ef9d004547bb","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/aside_archives.js","hash":"2ec66513d5322f185d2071acc052978ba9415a8e","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/page.js","hash":"c6611d97087c51845cb1ab4821696a62fa33daeb","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/inject_head_js.js","hash":"65f2442e04c4defd16e7c1e67701d3bb41d9577a","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/aside_categories.js","hash":"e00efdb5d02bc5c6eb4159e498af69fa61a7dbb9","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/button.js","hash":"b816ded1451f28c7c54151ffe6c259b110253ae3","modified":1627578032000},{"_id":"themes/butterfly/scripts/helpers/related_post.js","hash":"54b9324e3506dcc9c9991ef5e11e37e66e21594f","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/hide.js","hash":"f33858ffb9e88191e644796e11d2f901eb332308","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/gallery.js","hash":"94826ea6bcc4d2304199adae12c4e2b272caf529","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/inlineImg.js","hash":"a43ee2c7871bdd93cb6beb804429e404570f7929","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/mermaid.js","hash":"35f073021db93699fcac9ef351e26c59c31aadf7","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/label.js","hash":"03b2afef41d02bd1045c89578a02402c28356006","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/tabs.js","hash":"6c6e415623d0fd39da016d9e353bb4f5cca444f5","modified":1627578032000},{"_id":"themes/butterfly/scripts/tag/note.js","hash":"c16c6eb058af2b36bcd583b2591076c7ebdd51ad","modified":1627578032000},{"_id":"themes/butterfly/source/css/copyright.css","hash":"40f1b088daeaca4fdfe81e77024dd863aa5e7c1c","modified":1629187161084},{"_id":"themes/butterfly/source/css/background.css","hash":"24beaed4bf1eb5f2157743f0805e209e320374c0","modified":1631178433645},{"_id":"themes/butterfly/source/css/icon.css","hash":"d93c1e972e3569c9a08fedf3f54a1fe28f2b036f","modified":1631435783049},{"_id":"themes/butterfly/source/css/custom.css","hash":"662ddd3067ea440c511faeb917a5a0a49f38dda6","modified":1631535464054},{"_id":"themes/butterfly/source/css/index.styl","hash":"861998e4ac67a59529a8245a9130d68f826c9c12","modified":1627578032000},{"_id":"themes/butterfly/source/css/index.min.css","hash":"706e9fbfd4eace05e57d1d414b610f72a267480e","modified":1631105037871},{"_id":"themes/butterfly/.github/workflows/publish.yml","hash":"05857c2f265246d8de00e31037f2720709540c09","modified":1627578032000},{"_id":"themes/butterfly/source/css/mouse.css","hash":"a2d961444557cd7a72dc1d8e26c6f051e916249e","modified":1630154901393},{"_id":"themes/butterfly/source/css/side.css","hash":"5bce6787fa34f53e43a96a5c2700b510fb137b0d","modified":1630154791413},{"_id":"themes/butterfly/source/css/var.styl","hash":"40c3f64422205a24e68ce1ad8fe8163f24fdd525","modified":1627578032000},{"_id":"themes/butterfly/source/fonts/JetBrainsMono-Medium.woff2","hash":"f31bdf436d68c69503e96e7ca691a90ffa4918a8","modified":1630301034059},{"_id":"themes/butterfly/source/img/404.jpg","hash":"fb4489bc1d30c93d28f7332158c1c6c1416148de","modified":1627578032000},{"_id":"themes/butterfly/source/img/articles.png","hash":"f6654630e0f6f18497475b34e607f63a79f09140","modified":1630247726978},{"_id":"themes/butterfly/source/img/articles2.png","hash":"44f2e90e928bf1ae3fd91a85b70b5b1d07a91519","modified":1630247920935},{"_id":"themes/butterfly/source/img/algolia.svg","hash":"ec119560b382b2624e00144ae01c137186e91621","modified":1627578032000},{"_id":"themes/butterfly/source/img/avatar1.jpg","hash":"466d1b7cd0573e57e7db294c5f63c55ee6ca4296","modified":1630311556297},{"_id":"themes/butterfly/source/img/favicon.png","hash":"3cf89864b4f6c9b532522a4d260a2e887971c92d","modified":1627578032000},{"_id":"themes/butterfly/source/img/avatar2.jpg","hash":"4a7143ede9561ace432c2ac963b406428b296756","modified":1630313302374},{"_id":"themes/butterfly/source/img/mybatis.png","hash":"1e685804796630baba037812e3beab62936e1227","modified":1630246612746},{"_id":"themes/butterfly/source/img/huiji.png","hash":"f33b6927a0124ce7ad10638b96ee3be9b55c555e","modified":1630246963906},{"_id":"themes/butterfly/source/img/plane.png","hash":"fcd0b34a638838dcae8cbf17f34c51f973a0c02b","modified":1628982336217},{"_id":"themes/butterfly/source/img/pcbimg.png","hash":"72afcf1951d00c206d235facf6e09d16802c6ba1","modified":1630248671451},{"_id":"themes/butterfly/source/img/pcbimg2.png","hash":"511c4a15ad0f382adcbd3e1ff86dca3ead5d2df1","modified":1630487183495},{"_id":"themes/butterfly/source/img/mybatis1.png","hash":"d40a9abf2799be2e830868845e7287e884d8688d","modified":1630248123122},{"_id":"themes/butterfly/source/js/main.js","hash":"8ef2821ceb92d81aa0f8c02ee932f094adcafb2b","modified":1627578032000},{"_id":"themes/butterfly/source/js/tw_cn.js","hash":"00053ce73210274b3679f42607edef1206eebc68","modified":1627578032000},{"_id":"themes/butterfly/source/js/utils.js","hash":"8319b59c26ce8cd2b0ae7d030c4912215148fa92","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/autoload.js","hash":"e4364dd681c0a6d5bf64b50b94c3fa656303aa26","modified":1630559474816},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.json","hash":"cc68124da17a8547f26ebca0c0596d20dea37238","modified":1629353672566},{"_id":"themes/butterfly/source/live2d-widget/waifu-tips.js","hash":"2684e315f9aeb882055f541f943c959bd3afbe34","modified":1629353672566},{"_id":"themes/butterfly/source/img/plane1.png","hash":"c5713f3d92ddcd45d2b9a494f78b0f5b0e4a7466","modified":1628982320662},{"_id":"themes/butterfly/source/live2d-widget/waifu.css","hash":"003ddb4a25b804a9ce99c6f365142fa382dd0e75","modified":1629355252618},{"_id":"themes/butterfly/source/live2d-widget/LICENSE","hash":"1eba7caf09a39110ad2f542e3ed8700d1a69c6d3","modified":1629353672556},{"_id":"themes/butterfly/source/mouse/Cross.cur","hash":"6d6d6f975164d58db643a84ed238375fcbdac98d","modified":1564159338221},{"_id":"themes/butterfly/source/mouse/Handwriting.cur","hash":"195c938abc7f3e6d1f2e6301c46ba1e42ff99a4f","modified":1564159347886},{"_id":"themes/butterfly/source/live2d-widget/README.md","hash":"665054726a60b77a8f5e5b96527b8f1a4f166e30","modified":1629353672556},{"_id":"themes/butterfly/source/mouse/Help.cur","hash":"bf59506b7a65e2a5bb8c07b9b3c8daa6b90b0b7d","modified":1564159364356},{"_id":"themes/butterfly/source/mouse/Arrow.cur","hash":"1ab6471c728291cf66ba813b09135d48a06e74c3","modified":1564159324877},{"_id":"themes/butterfly/source/mouse/IBeam.cur","hash":"c96a55e806892deb9c1866c1aa33f98627d6dadf","modified":1564159375581},{"_id":"themes/butterfly/source/mouse/SizeAll.cur","hash":"b8ccca4354a8c11d61b7281818004933bdc32e01","modified":1564159388444},{"_id":"themes/butterfly/source/mouse/AppStarting.ani","hash":"e334f5489e63167adbd44f1116ffcfc72e84f718","modified":1564159318638},{"_id":"themes/butterfly/source/mouse/Hand.cur","hash":"46d6d4e0a8a79e054df35bcee1a53bb3c4b1ec8b","modified":1564159343139},{"_id":"themes/butterfly/source/mouse/SizeWE.cur","hash":"915ae8029726e0cf7b8f523fff059a96148cfc5c","modified":1564159419424},{"_id":"themes/butterfly/source/mouse/SizeNWSE.cur","hash":"25666d52fb83373286dc0b815387be41a52a26ac","modified":1564159413351},{"_id":"themes/butterfly/source/mouse/NO.cur","hash":"709b03659cd3be203e9d1c449d675e866847b957","modified":1564159381269},{"_id":"themes/butterfly/source/mouse/SizeNESW.cur","hash":"253f4e4c0da9b85757596c09187a79204fd9f07b","modified":1564159398663},{"_id":"themes/butterfly/layout/includes/head/Open_Graph.pug","hash":"6c41f49a3e682067533dd9384e6e4511fc3a1349","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/google_adsense.pug","hash":"95a37e92b39c44bcbea4be7e29ddb3921c5b8220","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/config_site.pug","hash":"889ef16fa34a39e5533bc170e62f20f3450cc522","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/analytics.pug","hash":"90d01b88d0f406d00184960b1afe9230aec2ebe6","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/config.pug","hash":"4def0aab9e2172ad1f29abd1535d8e08ff23aa0b","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/preconnect.pug","hash":"e55f8bdb876d5429a908498db1307b94094c0d06","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/pwa.pug","hash":"3d492cfe645d37c94d30512e0b230b0a09913148","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/noscript.pug","hash":"d16ad2ee0ff5751fd7f8a5ce1b83935518674977","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/head/site_verification.pug","hash":"e2e8d681f183f00ce5ee239c42d2e36b3744daad","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/nav.pug","hash":"c205b9fd72b2fe19e6d15c5b5ab0fb38c653032e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/social.pug","hash":"0d953e51d04a9294a64153c89c20f491a9ec42d4","modified":1627578032000},{"_id":"themes/butterfly/source/mouse/SizeNS.cur","hash":"4c02aa53675e758f61de6e13a5cf764b41e129f5","modified":1564159406614},{"_id":"themes/butterfly/layout/includes/loading/loading-js.pug","hash":"4cfcf0100e37ce91864703cd44f1cb99cb5493ea","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/loading/loading.pug","hash":"5276937fbcceb9d62879dc47be880cd469a27349","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/index.pug","hash":"65fa23680af0daf64930a399c2f2ca37809a8149","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/menu_item.pug","hash":"24370508ee87f14418e8f06e9d79ad8c52a342c4","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/header/post-info.pug","hash":"92f81a437c9db49f7ebcf608bc09488ecdb55a21","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/categories.pug","hash":"1f30952fed73dec21b42e2e30b7fe2e84618d2e4","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/default-page.pug","hash":"dbec869c62135695495703a29ad7655e9965d461","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/flink.pug","hash":"b53a2d4f9c37b375a4446d2273dcfb7712d91b3e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/page/tags.pug","hash":"93d4ebc7dc8228c7a10ddeb5a553d0dcdabbe145","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/mixins/article-sort.pug","hash":"bfd20a845dc4ac8d30792692954f3f404bb25ea0","modified":1629471437777},{"_id":"themes/butterfly/layout/includes/post/reward.pug","hash":"5b404356f311d2ee36478291ca3553210867b738","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/mixins/post-ui.pug","hash":"4c3c5cb69b3aead8c232cb0fbc251929f28aad75","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/aplayer.pug","hash":"292646dfab135973b09f0fa9e3931e83da2ed30e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/pangu.pug","hash":"d5fec7dedc52ab23865fb4db002755e9bdaadc9f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/post/post-copyright - 副本.pug","hash":"88e3b611b03149665e4113cfa39595c1a3fca7e5","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/post/post-copyright.pug","hash":"2ed4573caa447dd9eac1bf859a09e3618f7dc2c8","modified":1629186546309},{"_id":"themes/butterfly/layout/includes/third-party/prismjs.pug","hash":"1fbecfd299068f90d727f0c8c65e2a792fa6e3e2","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_ad.pug","hash":"60dc48a7b5d89c2a49123c3fc5893ab9c57dd225","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_archives.pug","hash":"86897010fe71503e239887fd8f6a4f5851737be9","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/effect.pug","hash":"b9d54a01d7c2a7a183cb7209e99430ce7fea1fe3","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_announcement.pug","hash":"3d8e3706a056389176f55dd21956aabc78046761","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/pjax.pug","hash":"933cb710d2dbcea25c6426a57c6f49d2f48b792c","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_bottom_self.pug","hash":"13dc8ce922e2e2332fe6ad5856ebb5dbf9ea4444","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_categories.pug","hash":"d1a416d0a8a7916d0b1a41d73adc66f8c811e493","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_newest_comment.pug","hash":"27afd2274bd5f2cbbf1bad9f0afe2b2b72c213ca","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/subtitle.pug","hash":"d50e5c22cd6bc3c378bc581918136746cfa3447f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_post_toc.pug","hash":"ae9336bf31cdad08ff586ead4295912a96563c76","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_tags.pug","hash":"438aea3e713ed16b7559b9a80a9c5ec0221263df","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_top_self.pug","hash":"ae67c6d4130a6c075058a9c1faea1648bcc6f83e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_author.pug","hash":"0366c658cdcff839aa1df2e2d252a03a53fd427e","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_recent_post.pug","hash":"9c1229af6ab48961021886882c473514101fba21","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/index.pug","hash":"b5525891f6affd02c1ea3b2327c026882efe428b","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/widget/card_webinfo.pug","hash":"0612aaee878f33ea8d3da0293c7dc3b6cd871466","modified":1627578032000},{"_id":"themes/butterfly/source/css/_global/function.styl","hash":"eda47f3e807a466ba8275627ea936c5100c43818","modified":1627578032000},{"_id":"themes/butterfly/source/css/_global/index.styl","hash":"e211efbd1952d4b1b881287aa43423133c1d166e","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/highlight.styl","hash":"85e72c70a0cef29e40be1968f5d23b06c6f8e3aa","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/theme.styl","hash":"fa7a4c1685f391d60ed863e869b9604b59746c27","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/404.styl","hash":"b0488ceacde74af139d66c8db5cb36cc21737b9b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/archives.styl","hash":"6874adc2e276443f354bbe50d0072e9bec37243c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/categories.styl","hash":"e554549f0a0ae85362f0b0e8687981741f486f6b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/common.styl","hash":"97fec1e814f88237862f4d800a35362b802f6625","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/tags.styl","hash":"9a881c031f463c486bd25248c2814fd09f97892b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/flink.styl","hash":"2cc49d3f6a6beb9f7bff93e292f88aa5681da1d0","modified":1627578032000},{"_id":"themes/butterfly/source/css/_page/homepage.styl","hash":"7c4152162a03aa8331a783df5695e4ebbb816a8c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_mode/darkmode.styl","hash":"4e629f510b73f998ab208b739c5bd7dcd168d1a7","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/chat.styl","hash":"29f48f9370f245e6e575b5836bccf47eb5688d8b","modified":1627578032000},{"_id":"themes/butterfly/source/css/_mode/readmode.styl","hash":"f59a9a0059d5261251bdd6de45aa97dd2d11e633","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/aside.styl","hash":"7feb755ca7c22da36bbad11e74ecd95fdcf3b879","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/categoryBar.styl","hash":"7e31831eef19bd1957b5b6b5a957c64f20ef703d","modified":1630592669826},{"_id":"themes/butterfly/source/css/_layout/footer.styl","hash":"dd8cdf639ba2b726437c77fa7aa8d5edbabe8f9b","modified":1627578032000},{"_id":"themes/butterfly/source/mouse/UpArrow.cur","hash":"2e65c3437b457ddf6429a096d5921ea962703843","modified":1564159425136},{"_id":"themes/butterfly/source/css/_layout/comments.styl","hash":"f1b63892baafa48ab872bc79671d57aafd511f6c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/head.styl","hash":"98235fcda3b87ad6f7e91eafbed94d0d6ae847ca","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/loading.styl","hash":"7d18a7be9cfea65091de3ef00014063d2d649912","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/pagination.styl","hash":"90fe01c968696a9f791cb2b84fca621cbbb56f47","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/post - 副本.styl","hash":"d748951d9fbcd04dda839085af78b01b8fa04cba","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/relatedposts.styl","hash":"0551c5893d1589a3d17ce161e50ecb1d724cc6e8","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/rightside.styl","hash":"7a072589e6097dbe942783131964f2372fdf1eb6","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/post.styl","hash":"72536fe1f905d0719896fd38c91d20040e3bc1c9","modified":1629187118515},{"_id":"themes/butterfly/source/css/_layout/reward.styl","hash":"ea1ba40dd5954c2ed718a126336fb7f94da4e66f","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/hexo.styl","hash":"d0386ba6d8d63afc72b9673e8f3e89df6446ffc2","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/third-party.styl","hash":"978c397d0966eaf9e6e2afd13866f8f4900b509f","modified":1627578032000},{"_id":"themes/butterfly/source/css/_layout/sidebar.styl","hash":"2c5fb77c448ce0a734040c8ce532b28fed688899","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/button.styl","hash":"1c3f9d7efc3b9dfcfa8926a1132d0c44ffc7d4b2","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/label.styl","hash":"f741e85295ce15c70a6027ec15a542636dd5dcca","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/hide.styl","hash":"21964fdd6d74ffbea519418bab65024aee5f3736","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/inlineImg.styl","hash":"df9d405c33a9a68946b530410f64096bcb72560c","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/gallery.styl","hash":"53ecae272e16223a436c497abbf25dd5f0fc4aaa","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/note.styl","hash":"86fee274a62f7f034547342930f445c47378eb55","modified":1627578032000},{"_id":"themes/butterfly/source/css/_tags/tabs.styl","hash":"1756791581c0ec51cb03353a09dac4778d944349","modified":1627578032000},{"_id":"themes/butterfly/source/css/_search/index.styl","hash":"f168f5c669978f633abe118cdcc4a12cfc883c01","modified":1627578032000},{"_id":"themes/butterfly/source/css/_search/local-search.styl","hash":"6befe4c51b86d0c1de130beeecad9e28d6442713","modified":1627578032000},{"_id":"themes/butterfly/source/css/_third-party/normalize.min.css","hash":"2c18a1c9604af475b4749def8f1959df88d8b276","modified":1627578032000},{"_id":"themes/butterfly/source/css/_search/algolia.styl","hash":"917e0e399e117217184ca63d3eb5c4843bcccf7b","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-1.png","hash":"4bd4d97a1fcaa5deb3cea1c0a102a895a15af32f","modified":1629353672556},{"_id":"themes/butterfly/source/js/search/local-search.js","hash":"459e2541afda483916d16fce4aaa56b41bcd42ba","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/fb.pug","hash":"7848ec58c6ec03243abf80a3b22b4dc10f3edf53","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/disqus.pug","hash":"d85c3737b5c9548553a78b757a7698df126a52cf","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/index.pug","hash":"e3bf847553515174f6085df982f0623e9783db7a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/valine.pug","hash":"bba9871f446c10ffcc8fa9023f5a2eb701a86bae","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/waline.pug","hash":"400ce038548d6f9ddb486150c724c87b6923a88b","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/chatra.pug","hash":"481cd5053bafb1a19f623554a27d3aa077ea59c3","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/card-post-count/twikoo.pug","hash":"ef1b2b5b980d6aeaa5d06b97d1afc9644b155a16","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/index.pug","hash":"3f05f8311ae559d768ee3d0925e84ed767c314d3","modified":1627578032000},{"_id":"themes/butterfly/source/js/search/algolia.js","hash":"65b45e61586f7e66c3f338370bfd9daadd71a4b7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/daovoice.pug","hash":"cfe63e7d26a6665df6aa32ca90868ad48e05ec04","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/crisp.pug","hash":"76634112c64023177260d1317ae39cef2a68e35f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/gitter.pug","hash":"d1d2474420bf4edc2e43ccdff6f92b8b082143df","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/chat/tidio.pug","hash":"24a926756c2300b9c561aaab6bd3a71fdd16e16d","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/disqusjs.pug","hash":"2e52c64e89f16267596a8465841dd46f51820982","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/disqus.pug","hash":"a111407fdcafcf1099e26ffa69786f8822c5d9fb","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/facebook_comments.pug","hash":"c46a932257212f82e4a9974fbbc5de8878c8b383","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/gitalk.pug","hash":"0b7571919e8ad51285deda56a1868fccf8c563d7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/index.pug","hash":"da9813f8dc0d388869c15413cf056012cfb69e1a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/js.pug","hash":"bafb3d5710824caa59a56017afb058fd2b4eac65","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/livere.pug","hash":"52ea8aa26b84d3ad38ae28cdf0f163e9ca8dced7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/utterances.pug","hash":"b871ea208e36398b4d668db9a9a0b61c79415381","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/twikoo.pug","hash":"16378d8646ea3f4ac99c18f0296dd85b13f9d775","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/index.pug","hash":"b8ae5fd7d74e1edcef21f5004fc96147e064d219","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/valine.pug","hash":"2b45fe09d5b591dca156b76dae99981f8d8e1c61","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/katex.pug","hash":"f9b00ead54573ba6e6eb33481588af144aab648d","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/mermaid.pug","hash":"3f3a3cd8bea2103dedd754f767aca5cb84d5f586","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/comments/waline.pug","hash":"36f3c603d2a2ecddaa6d2675a89d76ad94968f72","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/index.pug","hash":"f6506ccfd1ce994b9e53aa95588d0b6dbad11411","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/math/mathjax.pug","hash":"a47d8f9f593091cc91192c0c49deaa2c0d2317fd","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/disqus-comment.pug","hash":"b443d6b16baf3ea250041342cc0361a42a412b7f","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/github-issues.pug","hash":"34088a15655704d12e9b1807b47b3f6a860c9eec","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/search/algolia.pug","hash":"d8f59e94eafc669c49349561dc5bbea3915aecb7","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/twikoo-comment.pug","hash":"cb38ffe911023092a90a28f2ba8317a92b22cd0c","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/search/index.pug","hash":"da3b9437d061ee68dbc383057db5c73034c49605","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/add-this.pug","hash":"2980f1889226ca981aa23b8eb1853fde26dcf89a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/search/local-search.pug","hash":"613280d61b8ab9612014ec016ae3d3698d36fd1a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/valine.pug","hash":"59b4c26a827ace5a54855881d199977103ff6f50","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/index.pug","hash":"4c4a9c15215ae8ac5eadb0e086b278f76db9ee92","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/newest-comments/waline.pug","hash":"a2bc2601b7e0ae5caf1fc51a07390562d928620a","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/addtoany.pug","hash":"309f51bc5302e72fc469d54c577fbcfe57fb07a8","modified":1627578032000},{"_id":"themes/butterfly/layout/includes/third-party/share/share-js.pug","hash":"006acc91ce25fc7c7d778ca043e970f57dc46b83","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/demo/demo.html","hash":"a26089ebcf2606b5b38e71a1e68d64f9344cac1a","modified":1629353672556},{"_id":"themes/butterfly/source/css/_highlight/prismjs/index.styl","hash":"e0e7065124ef0d99f8322a47bc47838982e04ad0","modified":1630509500097},{"_id":"themes/butterfly/source/css/_highlight/prismjs/line-number.styl","hash":"8970cc1916c982b64a1478792b2822d1d31e276d","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/highlight/diff.styl","hash":"8c0726fb8d9a497d2f900b0be2845efaa68e3d87","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/highlight/index.styl","hash":"89cbcc8e087788ecec18b5fa58710afacdb7d080","modified":1627578032000},{"_id":"themes/butterfly/source/live2d-widget/demo/login.html","hash":"1fd46a102ccf4e89a483b85ef4bd79a563563836","modified":1629353672556},{"_id":"themes/butterfly/source/img/friend_404.gif","hash":"8d2d0ebef70a8eb07329f57e645889b0e420fa48","modified":1627578032000},{"_id":"themes/butterfly/source/css/_highlight/prismjs/diff.styl","hash":"5972c61f5125068cbe0af279a0c93a54847fdc3b","modified":1627578032000},{"_id":"themes/butterfly/source/mouse/Wait.ani","hash":"5a7278ded553b61a248ec549b9964b7e2930cecf","modified":1564159429851},{"_id":"themes/butterfly/source/live2d-widget/live2d.min.js","hash":"61a09b54a472b4d0541b7c50afb3dfdd172e062f","modified":1629353672566},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-3.png","hash":"f89c6fdf44008e7d3c7a1aafe65dc39b212edb6c","modified":1629353672556},{"_id":"themes/butterfly/source/live2d-widget/assets/screenshot-2.png","hash":"76b844653136184c573b3632e12296e008a7ff5c","modified":1629353672556},{"_id":"themes/butterfly/source/img/02.jpg","hash":"52276fbc7427eaf3facebf1e7cf9d99dad1664e8","modified":1630244205652},{"_id":"themes/butterfly/source/img/banner.png","hash":"e0b6d5d65d72bdfeb1b1bcfd1af340088d50fdfb","modified":1629110564595},{"_id":"themes/butterfly/source/img/01.jpg","hash":"455aff642579a632713b90c58edcd89f1ea01aef","modified":1629129110293},{"_id":"themes/butterfly/source/img/34.jpg","hash":"10b2ddfbe7f03c4513ace7a2855a47f907478d55","modified":1630244370887},{"_id":"themes/butterfly/source/img/blissed01.jpg","hash":"932db4b0cb8a297a4f9c1fd48af59183db742584","modified":1630244182581},{"_id":"themes/butterfly/source/img/categories.png","hash":"1829d3667c9fb36d729057a2fb26f12b662c86e4","modified":1629111056814},{"_id":"themes/butterfly/source/fonts/shishangblack.ttf","hash":"b5dda0beb2618829e41553d0dc656afbc78af523","modified":1630200705629},{"_id":"themes/butterfly/source/fonts/ajLangMan.ttf","hash":"c79e63ca78049f8273c0de7b5c65898c1dd8557f","modified":1629378758891},{"_id":"themes/butterfly/source/fonts/loving.ttf","hash":"786ab7558a8a2cd5de1ac71a1c6c19bfeff17e95","modified":1630200693598},{"_id":"themes/butterfly/source/fonts/ZhuZiAWan.woff2","hash":"a8512c6d5d70f5fc098a58a01a0ad720aa211c2a","modified":1631094979211},{"_id":"public/manifest.json","hash":"e5653018c633c03092c691caf2b17921c60fe633","modified":1631633856066},{"_id":"public/search.xml","hash":"6c5cefd2b310b7656e3e459e7b0e7403496dd38a","modified":1631633856066},{"_id":"public/404.html","hash":"58e406587db3548d33d3249886af5dfd2184989b","modified":1631633856066},{"_id":"public/about/index.html","hash":"f996050c1b8d596c9e3eb62b844982d16c705354","modified":1631633856066},{"_id":"public/link/index.html","hash":"3b9a75e10a18bd16eeb4cc20b268ccdc1154cb20","modified":1631633856066},{"_id":"public/music/index.html","hash":"06b0e469a43d9c6106c6c747688d2c70ffaed9df","modified":1631633856066},{"_id":"public/categories/index.html","hash":"118251e34b7c66b9fffb5c11e190b8a502340816","modified":1631633856066},{"_id":"public/notes/index.html","hash":"2a8ebaaa7b34885bf46975ce47271c15376a2c4b","modified":1631633856066},{"_id":"public/tags/index.html","hash":"f27805e42397220cc3da0d6c75998ebde260ffe1","modified":1631633856066},{"_id":"public/poems/index.html","hash":"43bf0ab218e30404765cf9c6856609221af24068","modified":1631633856066},{"_id":"public/posts/ee4b2b4d.html","hash":"8d72917cfce0212eb47acd0741fa39f2b6f463c9","modified":1631633856066},{"_id":"public/web-links/index.html","hash":"d535c4b5620fad3790ba92c6ee0641255846c328","modified":1631633856066},{"_id":"public/posts/88ad757.html","hash":"e2c6b65699d1ae25bb7326977c592940610bea1c","modified":1631633856066},{"_id":"public/posts/8b75069d.html","hash":"1d88cc36293dcdc8aaed858046a09f9ca9299bf0","modified":1631633856066},{"_id":"public/posts/1cd3002f.html","hash":"4233127e9c2c23aec8e5a96562a10729d76dcdd3","modified":1631633856066},{"_id":"public/posts/7dc1e032.html","hash":"dd8bdb3613e5ada905a2303e4f0a20dd70bc3ad6","modified":1631633856066},{"_id":"public/posts/f8dae6c5.html","hash":"1958ecfe80d2ab434268ebb6d92eab79269b420f","modified":1631633856066},{"_id":"public/posts/1294a4bd.html","hash":"48dd307b9e356f06fa0f34705d134ff0404f7573","modified":1631633856066},{"_id":"public/posts/190b30be.html","hash":"0a6066123a28e82d23c126603dd94029f5b7a860","modified":1631633856066},{"_id":"public/posts/a4c109bd.html","hash":"8b62e61ea8c3d924a7899598ee3e90b082b94702","modified":1631633856066},{"_id":"public/posts/54667693.html","hash":"6eb8e590c7c95e6b7a4b1c41f67e71e395af019a","modified":1631633856066},{"_id":"public/posts/401491fc.html","hash":"3f36bb80cfce6539e641a434a813461e982300ab","modified":1631633856066},{"_id":"public/posts/3de0f4a2.html","hash":"70d982cabab55ff24be5dee1668a0064b36427db","modified":1631633856066},{"_id":"public/posts/54a4218.html","hash":"3f61abfc0f2e9c6f1e5818e88f8144c60b815c8c","modified":1631633856066},{"_id":"public/posts/446faab7.html","hash":"f9bb5a58323e604583e6972b4327cd007b1068d7","modified":1631633856066},{"_id":"public/posts/ecec7d3f.html","hash":"f7a6f90bd0bd011ecff7a1ae78e8b4a5237c0999","modified":1631633856066},{"_id":"public/posts/1eb33081.html","hash":"752450393018314ba436dd83174188cec39a814c","modified":1631633856066},{"_id":"public/posts/57ac54fe.html","hash":"840bf899083f71fbaad61d0924b1483082f2a7e7","modified":1631633856066},{"_id":"public/posts/b8426075.html","hash":"04afceb2bf3a32a62bec0e50d7834ac94c2bc864","modified":1631633856066},{"_id":"public/posts/a4682555.html","hash":"c74ff055138550853d2b10c4c2786f86f2f1b90a","modified":1631633856066},{"_id":"public/posts/890c8bdb.html","hash":"5a095ce108ea77900ff3f1dede8b8c3e9aa110cc","modified":1631633856066},{"_id":"public/posts/c8eb4af9.html","hash":"1015211fc45492a3cc728dc820119856fe769505","modified":1631633856066},{"_id":"public/posts/dc7f8ae1.html","hash":"dd08a553296eca1cd503fafdfec55b2de37191fc","modified":1631633856066},{"_id":"public/posts/6e81c43e.html","hash":"9dff365ca5e2ca3018c9e24b4c1883a47dca57a8","modified":1631633856066},{"_id":"public/posts/3b32a20d.html","hash":"0cde59bc14487f5e042ef11ebab2939318469ad9","modified":1631633856066},{"_id":"public/posts/81ecdf4a.html","hash":"2a19a3f511e557417d497c8fcdf6550f0c0774aa","modified":1631633856066},{"_id":"public/posts/ba43a8dc.html","hash":"6212939774785dc57ce61487108b65aca471a997","modified":1631633856066},{"_id":"public/posts/f218ca42.html","hash":"15848001e001d4294889a0192f352aedf901a53a","modified":1631633856066},{"_id":"public/posts/ab69c7ec.html","hash":"0464e8ae29728e7233613df9f1b7b32ac471213f","modified":1631633856066},{"_id":"public/posts/78903d87.html","hash":"1abf47c7de03d672c2b052b0fcd47d89985cdc5e","modified":1631633856066},{"_id":"public/posts/17528481.html","hash":"635f9377b2af5bbd1c10086cb56b7af7fbfa374f","modified":1631633856066},{"_id":"public/posts/b836e65.html","hash":"899c0ee9cc3a85afb1f7a468acf90110a378639f","modified":1631633856066},{"_id":"public/posts/c2d1d169.html","hash":"ab4f3b09ca9fe2d5cec97d4dc79aa3848185ecb1","modified":1631633856066},{"_id":"public/posts/5dd9c9e3.html","hash":"f30ded54e22b1fceead36f2e2a5dd5c3e019a3ca","modified":1631633856066},{"_id":"public/posts/c4d68191.html","hash":"18d8daa44348d26765dc9c712348e31f8a0be08b","modified":1631633856066},{"_id":"public/posts/c27dd6ec.html","hash":"cf0de529f9f84bc11beb034ea11b1776af6f8365","modified":1631633856066},{"_id":"public/posts/383e39e3.html","hash":"021dfc87842ab104e9c0d30865925e1c267933c2","modified":1631633856066},{"_id":"public/posts/46cd19c8.html","hash":"026c49121699da6c7fc2f088b31ee4c37199361f","modified":1631633856066},{"_id":"public/posts/9e0a8624.html","hash":"f374ce85021f993616b98cbcb0f732d92fa56668","modified":1631633856066},{"_id":"public/posts/894e18ee.html","hash":"4d0f636e99cfc36b3744679343a46adc32da2cce","modified":1631633856066},{"_id":"public/posts/adfcabaa.html","hash":"35c9c2f7c7e6f3ac48d5c6d9fa5ca600027d413f","modified":1631633856066},{"_id":"public/posts/73ba8569.html","hash":"986c05360940fbd4b8bd2bde272c0d5f3a665f75","modified":1631633856066},{"_id":"public/posts/1b6a7930.html","hash":"b500d5c03f2cd0eb749287a9cd3358e45dc2c756","modified":1631633856066},{"_id":"public/posts/c25015c5.html","hash":"1d683c51cbc3bafb2ef0c86de378eac6a540cddb","modified":1631633856066},{"_id":"public/posts/baad5185.html","hash":"38344a849c2cb8105f352d24a5f50e8a13a6520b","modified":1631633856066},{"_id":"public/posts/2d7f83ef.html","hash":"671a3d3c537097a02c6433796db424da86beb86f","modified":1631633856066},{"_id":"public/posts/d150c3f1.html","hash":"c1c8faaadbfa1dbc74f6aa851f67ba90aca2817e","modified":1631633856066},{"_id":"public/posts/2080031a.html","hash":"8c6c1e6d3d1d6168b715f00829bfdbec4d40e569","modified":1631633856066},{"_id":"public/posts/27153554.html","hash":"fd9c69f99d550f23f4022eaa14a422278c7f5d76","modified":1631633856066},{"_id":"public/posts/4dc0a7a8.html","hash":"9e5ce406873762f90541189bcb77718568d4be69","modified":1631633856066},{"_id":"public/posts/89cf4bca.html","hash":"01069c939864e7b1e4c2952faa2d42c2d571e684","modified":1631633856066},{"_id":"public/posts/a1a1eb5.html","hash":"57cd8daacd3ff4d4e0a132d13a0a52475f261d4c","modified":1631633856066},{"_id":"public/posts/be39d4e8.html","hash":"bee976b1bf03fac9080b30a939c8f1fa9c775246","modified":1631633856066},{"_id":"public/posts/26c04051.html","hash":"45f844b331497a4c93752c8ee47dea07c82aef5f","modified":1631633856066},{"_id":"public/posts/38b3e585.html","hash":"b182f51537f07fcf9e8c1207d33d187cdc0c1985","modified":1631633856066},{"_id":"public/posts/39f00355.html","hash":"2e4539e7c805a0449b314be125dbd82343fe575e","modified":1631633856066},{"_id":"public/posts/f68104ec.html","hash":"fba07f1b3374a419d3fc34f3519bc77e1a262950","modified":1631633856066},{"_id":"public/posts/3feecb5e.html","hash":"8bd533a97d3d7c3f351745c0894f5fa623d17377","modified":1631633856066},{"_id":"public/posts/7f2244ba.html","hash":"b6733cbc60db2edcd6e54a1eea52afc59e88c911","modified":1631633856066},{"_id":"public/posts/d8642fb9.html","hash":"ca62d808348a78f38227ac0e69cdb939c67f0204","modified":1631633856066},{"_id":"public/posts/3fb3b16d.html","hash":"813ec5ece159676140fec6d31021763f0765f117","modified":1631633856066},{"_id":"public/posts/d959138b.html","hash":"7b572164e53e13a48e1c6329abf6549fe17ca910","modified":1631633856066},{"_id":"public/posts/59e405f3.html","hash":"fd6e165bb6b6c2bab7183b91e71c41a85991f7e7","modified":1631633856066},{"_id":"public/posts/752d4bdb.html","hash":"0dcb911913475c61dd4df6b86effa2e2fb50bd06","modified":1631633856066},{"_id":"public/posts/3afd7950.html","hash":"6154496d361851690e16f347f75a63f997634b04","modified":1631633856066},{"_id":"public/posts/530cfffd.html","hash":"d5f009bd083d94f216f9084caf80bc75b2e38983","modified":1631633856066},{"_id":"public/posts/3f4cddeb.html","hash":"dc99bec7ffa43bcde7b8d315dac042fb0c9573b5","modified":1631633856066},{"_id":"public/posts/1a57c6b6.html","hash":"7d290b2df71f7a7bc589c62f80b2584412b55fc9","modified":1631633856066},{"_id":"public/posts/d29e4d17.html","hash":"62dac627c7b813865909b4da59e9c2d78aa27348","modified":1631633856066},{"_id":"public/posts/2a3cf99e.html","hash":"656b6ce8849f4497a362d04801c2cc8c01ceabe5","modified":1631633856066},{"_id":"public/posts/683d1a78.html","hash":"1b0c01572589a576b1a062bab76674f6cffe4e02","modified":1631633856066},{"_id":"public/posts/d709dda.html","hash":"ce9df1dfc884deeaa312e3eaf9293051e57c7c9b","modified":1631633856066},{"_id":"public/posts/39aef30f.html","hash":"2ab0b61161357d59b42e9282294f8a67690decb2","modified":1631633856066},{"_id":"public/posts/b9bb0bd5.html","hash":"8882192a09fa15f9bb58649c084b61d387dd8587","modified":1631633856066},{"_id":"public/posts/af3c95f0.html","hash":"9d89b51dbee638317273dd94a302bc503f2c9f63","modified":1631633856066},{"_id":"public/posts/bdfc8ea1.html","hash":"3ab6e1c5ca7b13791351c1abba88b9d002919a6f","modified":1631633856066},{"_id":"public/posts/79c0e989.html","hash":"9c9e72fc2d4d01597e00352faadea3d10d85103b","modified":1631633856066},{"_id":"public/posts/52e2375.html","hash":"d03264d3de06bdb89eb0c0a33db3475b1cd1b569","modified":1631633856066},{"_id":"public/posts/374aa28c.html","hash":"301d8688b2c88c5f1ffa1fe435e8732aeb10e65a","modified":1631633856066},{"_id":"public/posts/1a9b2152.html","hash":"be3bcfb2c1a8c4b5e3820e24b61a379e5412f86a","modified":1631633856066},{"_id":"public/posts/4609d38b.html","hash":"907eff716d4015e52f359d9201de10527b8c2593","modified":1631633856066},{"_id":"public/posts/e7ba7594.html","hash":"f8a5d307059fa0d275228864c3eceecccaaf763d","modified":1631633856066},{"_id":"public/posts/345d8e17.html","hash":"cf57b589bc3f9745b92e1d75f66941e6f2da1bce","modified":1631633856066},{"_id":"public/posts/170e1590.html","hash":"6d4845a6fb049215d8f320892387e4993d4deb95","modified":1631633856066},{"_id":"public/posts/c9f91dab.html","hash":"75fe236ecffedfdb0d9ca19dc445b3b7815bbed6","modified":1631633856066},{"_id":"public/posts/c5d59ae.html","hash":"0dbfcdd4761f1020e8bc6a9ed4435fabc2f59353","modified":1631633856066},{"_id":"public/posts/e0bbd322.html","hash":"cf01d97e38f8aa6198854d446c0e0b57fb90bf7f","modified":1631633856066},{"_id":"public/posts/c0626013.html","hash":"928bd3cbdb1292f4a25b0b142367ef3800cfd261","modified":1631633856066},{"_id":"public/posts/9dc8e4af.html","hash":"c912829f4478f39e6d1a2c7de26a6329b0c50dea","modified":1631633856066},{"_id":"public/posts/4f0e7b50.html","hash":"ec56a4d038522edb58d9fef79e3b4c8313de3c8f","modified":1631633856066},{"_id":"public/posts/f5c95c.html","hash":"3878be1111006dfab543cf265d933e10ec3d9b1d","modified":1631633856066},{"_id":"public/posts/ab09b476.html","hash":"53c83764ad4aa611379f4d980349e2b580178ca2","modified":1631633856066},{"_id":"public/posts/b3cec184.html","hash":"625e998ef8e74a970ea998a04baee33b5b59c2b2","modified":1631633856066},{"_id":"public/posts/3aac01ca.html","hash":"58b9c1ace0157361d90d5fd95050a228c4185ad9","modified":1631633856066},{"_id":"public/posts/5e64b19a.html","hash":"0930d5ed8b1496d2da1a7aefea2f79ff04f83a3e","modified":1631633856066},{"_id":"public/posts/38b36403.html","hash":"4c158a9b59335c192ddf512b514c7f59d49cd635","modified":1631633856066},{"_id":"public/posts/94566522.html","hash":"46947a24b755977cb6287517dbc6883f2e29c9aa","modified":1631633856066},{"_id":"public/posts/b54f36d8.html","hash":"15857f756806f2b7b5f68a1dd7db8e18c22cfbda","modified":1631633856066},{"_id":"public/posts/753dfa3e.html","hash":"516213b5208a2e98096a6851eabf579083b5595e","modified":1631633856066},{"_id":"public/posts/63036c3c.html","hash":"d65b1b4675a9a7569fce7f534106de633dfccdc1","modified":1631633856066},{"_id":"public/posts/8a45e6a3.html","hash":"e2e873dfab3d9757d86f87bb78e2571524dd1cd4","modified":1631633856066},{"_id":"public/posts/68f2c599.html","hash":"9d9f42efb5244e6bd19232c59025fe165523ad35","modified":1631633856066},{"_id":"public/posts/c63fec2a.html","hash":"74ec8ba9f848be9fab846e232034eefdd3593ab3","modified":1631633856066},{"_id":"public/posts/52da8eea.html","hash":"c3002fea62a2bacaf72433b2a04c35be31d8f780","modified":1631633856066},{"_id":"public/posts/51a2ab89.html","hash":"312e59ffe8193710fd26a3c9de94dcb91c54cbd9","modified":1631633856066},{"_id":"public/posts/4515cdfc.html","hash":"7055bc1ea114a702b30ba2fb498c476788cb988f","modified":1631633856066},{"_id":"public/posts/52b44ec7.html","hash":"dda522576c0659a99584a597af2ad9302a5cd863","modified":1631633856066},{"_id":"public/archives/index.html","hash":"d63799f0f677d7c83c84d6ca525367a0fef7f0a2","modified":1631633856066},{"_id":"public/archives/page/2/index.html","hash":"97b487f9dc7a26267090e0e0aa0926cd6554f47e","modified":1631633856066},{"_id":"public/archives/page/3/index.html","hash":"9ec1c1771331caff2e752d663843fbf47d9aeec5","modified":1631633856066},{"_id":"public/archives/page/4/index.html","hash":"81fdd2d50278aad738dc6e08854a8383daa1e507","modified":1631633856066},{"_id":"public/archives/page/5/index.html","hash":"9c3714a0b98b49d94468153530deefd13bbae5d7","modified":1631633856066},{"_id":"public/archives/page/6/index.html","hash":"bfb7543d14a274c93e96c69a4bd50f9c32b8d083","modified":1631633856066},{"_id":"public/archives/page/7/index.html","hash":"68b36bd61b4d4964e7d0964862fe9a40dabee5fd","modified":1631633856066},{"_id":"public/archives/page/8/index.html","hash":"5134324fd8056f4ca2a5a89c972dd9a0e1a1bb1f","modified":1631633856066},{"_id":"public/archives/page/9/index.html","hash":"3e0953b96746f3fdefac23c46bc7b92eb9de82c6","modified":1631633856066},{"_id":"public/archives/page/10/index.html","hash":"4bb0eee002c1c0be43e00351f8768cea330393e6","modified":1631633856066},{"_id":"public/archives/page/11/index.html","hash":"243cdbe7c3510b0e67fa837103ef8895bcabcb75","modified":1631633856066},{"_id":"public/archives/2021/index.html","hash":"aed4dff4f7eabba3f598b8366d22cdce7e303aa7","modified":1631633856066},{"_id":"public/archives/2021/page/2/index.html","hash":"8d92a763323c7e1e651817967dbfc04fa349f287","modified":1631633856066},{"_id":"public/archives/2021/page/3/index.html","hash":"0ef2d816fd7dafe857cab558945632323566c9d2","modified":1631633856066},{"_id":"public/archives/2021/page/4/index.html","hash":"6e0523af12e62414b2ee617e6788ceddd292763f","modified":1631633856066},{"_id":"public/archives/2021/page/5/index.html","hash":"9368fa0707de91160d921d3fe526dc8464fe8e9c","modified":1631633856066},{"_id":"public/archives/2021/page/6/index.html","hash":"9096c0a325a74f257d889d8c59811f0570c677e8","modified":1631633856066},{"_id":"public/archives/2021/page/7/index.html","hash":"c145880d3746c753a7568cc9450eca5f141c4e3b","modified":1631633856066},{"_id":"public/archives/2021/page/8/index.html","hash":"13c3c686fb758361db669f05bbc1f796dd72aef0","modified":1631633856066},{"_id":"public/archives/2021/page/9/index.html","hash":"f2c9de409a465c904cef11b8e8eb9c24f4bf44bd","modified":1631633856066},{"_id":"public/archives/2021/page/10/index.html","hash":"fa8c30c321463828963cc63a12ab2b52221bee87","modified":1631633856066},{"_id":"public/archives/2021/page/11/index.html","hash":"a5ed686ffc885a3c30e66532b5a3605bd19dd18a","modified":1631633856066},{"_id":"public/archives/2021/02/page/2/index.html","hash":"d7ba72d3ec237b08868f6da77d0cc9adf03b2a16","modified":1631633856066},{"_id":"public/archives/2021/02/index.html","hash":"0ccee5fa890a0afe16f6fc70269d8bfeb2a802e7","modified":1631633856066},{"_id":"public/archives/2021/02/page/3/index.html","hash":"6234cc5534859f5cb625375d19c50197af44d080","modified":1631633856066},{"_id":"public/archives/2021/04/index.html","hash":"3d2cfbfe660476858f18df5d3ef6b5dbcbc9e5ea","modified":1631633856066},{"_id":"public/archives/2021/05/index.html","hash":"c7e87b5418e60e671c08b3215b11ded7c2ff0bea","modified":1631633856066},{"_id":"public/archives/2021/06/index.html","hash":"520feefe0bbf26618f87443db4f82dacbd1dc588","modified":1631633856066},{"_id":"public/archives/2021/07/index.html","hash":"fae42ce426a1e6b6131ef1e5d978522bada35050","modified":1631633856066},{"_id":"public/archives/2021/07/page/2/index.html","hash":"bf4259c12b5736065d91e88d600cd57a173d8a59","modified":1631633856066},{"_id":"public/archives/2021/08/index.html","hash":"4378387e033c92810936da38d83ef5e7ec2f80e9","modified":1631633856066},{"_id":"public/archives/2021/08/page/2/index.html","hash":"ea75b482085cea17b3c33d90fad21b5c0bdfac51","modified":1631633856066},{"_id":"public/archives/2021/08/page/3/index.html","hash":"9b820787545efebbdef7ad12916ba4e5a9e26c00","modified":1631633856066},{"_id":"public/archives/2021/09/index.html","hash":"1be93dd46feb5ef99ef3c0c1eddbf56150638306","modified":1631633856066},{"_id":"public/archives/2021/09/page/2/index.html","hash":"e0909354dfada8ba1400bba0cdf5793de875df85","modified":1631633856066},{"_id":"public/categories/硬件学习/index.html","hash":"6a513b2a67c272a769bb6878eba5665fc167c0de","modified":1631633856066},{"_id":"public/categories/硬件学习/page/2/index.html","hash":"e9be192a35daa186ca3887a5a4543ac72b8bf49b","modified":1631633856066},{"_id":"public/categories/硬件学习/page/3/index.html","hash":"035bc3639d947d047fdded4dee18e3aef129947f","modified":1631633856066},{"_id":"public/categories/硬件学习/51单片机/index.html","hash":"f7af8ae920e8f5322dc8b98535279adb7d767a8b","modified":1631633856066},{"_id":"public/categories/Hexo/index.html","hash":"41825b4793ba33e31f0a795bfb423cf8739a724e","modified":1631633856066},{"_id":"public/categories/java/index.html","hash":"7c969a7ded506e20259de863de0cc68cd87478dc","modified":1631633856066},{"_id":"public/categories/java/page/2/index.html","hash":"3742eb83c1d8810af0a92ad62995dd1d2c254c50","modified":1631633856066},{"_id":"public/categories/java/page/3/index.html","hash":"db4f3020f34b31b06ed847037ebf4ab51ccf3a53","modified":1631633856066},{"_id":"public/categories/java/page/4/index.html","hash":"366da5957d294c82e392ade1a3db201b442feafb","modified":1631633856066},{"_id":"public/categories/java/page/5/index.html","hash":"2527d33a961f30c84ddf60e9a4409661f982d90e","modified":1631633856066},{"_id":"public/categories/java/page/6/index.html","hash":"bbaf8f7136f06bf796c5b158409e2375e1d48f9c","modified":1631633856066},{"_id":"public/categories/java/page/7/index.html","hash":"9a250026ace06bf39aedfb8590f4b9a9f07c1857","modified":1631633856066},{"_id":"public/categories/java/spring/index.html","hash":"4f42cea914f8cfcee7d4ea4958981d9b8c316067","modified":1631633856066},{"_id":"public/categories/java/spring/page/2/index.html","hash":"d731115057fcfc84b3043a452c0394e38dbc1fe8","modified":1631633856066},{"_id":"public/categories/java/java面试/index.html","hash":"ddb813a2c2a140287becfd75254266db228e3517","modified":1631633856066},{"_id":"public/categories/java/Maven/index.html","hash":"02e396a361742fe8247c33d00b8316c26d729025","modified":1631633856066},{"_id":"public/categories/java/Mybatis/index.html","hash":"52163b938fc3e972740512866de07be376ad2727","modified":1631633856066},{"_id":"public/categories/java/MySQL/index.html","hash":"0cc0c99dae4e1e2a82d9409fac856bd6aff0f754","modified":1631633856066},{"_id":"public/categories/硬件学习/esp8266/index.html","hash":"0bfa22368fd6da59eaabbb2a8c12ba1c89583c3a","modified":1631633856066},{"_id":"public/categories/硬件学习/物联网开发/index.html","hash":"2ed8a94c707cef5e188d594250a312b56c9a101d","modified":1631633856066},{"_id":"public/categories/java/springMVC/index.html","hash":"29c74e1664fb0ff84d5b3ca0f807f4c27965da58","modified":1631633856066},{"_id":"public/categories/java/javaEE/index.html","hash":"94402c66d020a587e916f3239a158cf0e08ccdd3","modified":1631633856066},{"_id":"public/categories/java/javaSE/index.html","hash":"3b5ea63a00cb05334ca9666cc3b07c736b6893e1","modified":1631633856066},{"_id":"public/categories/java/javaSE/page/2/index.html","hash":"c2a80c3af933daaf85b6703e2e3ea4da3cdac239","modified":1631633856066},{"_id":"public/categories/java/javaSE/page/3/index.html","hash":"690743c4d0cd73ecb6589e283232de9386db51b0","modified":1631633856066},{"_id":"public/categories/win10/index.html","hash":"9e86a6126de017f8134c2ec648b2ac36fb149814","modified":1631633856066},{"_id":"public/categories/硬件学习/stm32/index.html","hash":"1a67a33a38c3b171c7351568abb12cae1babc303","modified":1631633856066},{"_id":"public/categories/杂七杂八/index.html","hash":"438a4d1d0df06c74c0ed89264052ce97e913fa62","modified":1631633856066},{"_id":"public/categories/电磁场/index.html","hash":"02ba820d83001fe395004b9c39db5c947d123b67","modified":1631633856066},{"_id":"public/categories/网络通信/index.html","hash":"abafb1eace5d749d0d6fb907f726178201ccb74c","modified":1631633856066},{"_id":"public/categories/网络安全/index.html","hash":"ad7729956076715a209fb189782ec355fd5235b6","modified":1631633856066},{"_id":"public/categories/电磁场/矢量/index.html","hash":"f2a13ddb98d5f868c6c7469e26a7579958d5e6af","modified":1631633856066},{"_id":"public/index.html","hash":"3c7f2494f84dfa0fe16cd98d54ce6472b38e62a4","modified":1631633856066},{"_id":"public/page/2/index.html","hash":"d75c2b63057b80bae4d0174a62e01fff63dbc4b4","modified":1631633856066},{"_id":"public/page/3/index.html","hash":"419ebe03ae47eade03c63a99c43b12b235da77eb","modified":1631633856066},{"_id":"public/page/4/index.html","hash":"318efec312561a848d6fd2acb63d56637223cbb9","modified":1631633856066},{"_id":"public/page/5/index.html","hash":"11cf71182f0b050d05f07a4cf849bfa7985a5bc8","modified":1631633856066},{"_id":"public/page/6/index.html","hash":"2429e3eeac682aaeb4fea5b93c0d9b1fc26cad96","modified":1631633856066},{"_id":"public/page/8/index.html","hash":"0d27acda4c595eff4f073fb76363abd23766dd04","modified":1631633856066},{"_id":"public/page/7/index.html","hash":"cd5ad163189ab7ed849e35817bac94450eb72193","modified":1631633856066},{"_id":"public/page/9/index.html","hash":"3ef6fff0220a9aa0393b7211b82d9db61d6d9383","modified":1631633856066},{"_id":"public/page/10/index.html","hash":"c0bca5813654a92b10dc13bf0b975c820a41c593","modified":1631633856066},{"_id":"public/page/11/index.html","hash":"664f388f714426c7fc44542fd5f2acec530dba7a","modified":1631633856066},{"_id":"public/tags/51单片机/index.html","hash":"0548d7173c6b349dd77ae6ee429ac64b018d0905","modified":1631633856066},{"_id":"public/tags/Hexo/index.html","hash":"213a235becac8754f46baf7e8ff0c7c222e62847","modified":1631633856066},{"_id":"public/tags/Java面试/index.html","hash":"5f8055c1576891d5db2d2d217e27862143d22128","modified":1631633856066},{"_id":"public/tags/Maven/index.html","hash":"9d9adcbc7d74da9265116289163737f36876889e","modified":1631633856066},{"_id":"public/tags/MySQL/index.html","hash":"a177013670eb01837aa1a3de4dad74887b831997","modified":1631633856066},{"_id":"public/tags/spring/index.html","hash":"4dcbf62f17a585ef4a97884db24a0103d31978a8","modified":1631633856066},{"_id":"public/tags/spring/page/2/index.html","hash":"7affd12b7d8bc048a9060926fa909d8efc18e338","modified":1631633856066},{"_id":"public/tags/Mybatis/index.html","hash":"754a292da7ddc8e687974835ab90f8d4be187f74","modified":1631633856066},{"_id":"public/tags/esp8266/index.html","hash":"0264f427ca96d137607c2d682d4ff8778f324115","modified":1631633856066},{"_id":"public/tags/esp8266/page/2/index.html","hash":"629ca0e4fa7183e4af14370db512fd6afbdcb0bb","modified":1631633856066},{"_id":"public/tags/物联网/index.html","hash":"9309f73eb4c1552a0484eb8704b32420ccd4e842","modified":1631633856066},{"_id":"public/tags/springMVC/index.html","hash":"f531a754d87db249e79c5fd5f52b3e04db1c3135","modified":1631633856066},{"_id":"public/tags/Servlet/index.html","hash":"dfcb78a4427f5afb79d184ccfacdb55395aea2d5","modified":1631633856066},{"_id":"public/tags/Tomcat/index.html","hash":"a32b17c0384ec973148ebd31c0e40f841e8af7ea","modified":1631633856066},{"_id":"public/tags/javaSE/page/2/index.html","hash":"a33c49319f09534adb289ae8361f1612668d98e8","modified":1631633856066},{"_id":"public/tags/javaSE/page/3/index.html","hash":"deb72538264430394c65e91c0e5001e8a9939c88","modified":1631633856066},{"_id":"public/tags/javaSE/index.html","hash":"e0f9f158422ed596b1850cfea6bcaefce4d4c8fb","modified":1631633856066},{"_id":"public/tags/Http/index.html","hash":"a0143c82839fd1c5fd160b8e3bf14708e7f1bf28","modified":1631633856066},{"_id":"public/tags/win10/index.html","hash":"5c10df9f6c64f89352297e573b16277067e870af","modified":1631633856066},{"_id":"public/tags/MDK-keil5/index.html","hash":"9fc5a415ee288ed150092f6166684c6c7647a233","modified":1631633856066},{"_id":"public/tags/stm32/index.html","hash":"2a3cac3efadf3e057293efd9e4a5c0f2fabc399a","modified":1631633856066},{"_id":"public/tags/图床/index.html","hash":"038ae4bea02f30e45606f15a1fc3b91d8c955aaf","modified":1631633856066},{"_id":"public/tags/矢量/index.html","hash":"4dc07818b6f6ce8d5d971cd6802c6b3a5d2b26c7","modified":1631633856066},{"_id":"public/tags/网络安全/index.html","hash":"e4ce6e5fbbd10007db75288b9f3e3fb0e5fa599a","modified":1631633856066},{"_id":"public/tags/网络通信/index.html","hash":"e54eabdca13bb05f903b757364afaf24232e56b0","modified":1631633856066},{"_id":"public/img/404.jpg","hash":"fb4489bc1d30c93d28f7332158c1c6c1416148de","modified":1631633856066},{"_id":"public/img/algolia.svg","hash":"ec119560b382b2624e00144ae01c137186e91621","modified":1631633856066},{"_id":"public/img/articles.png","hash":"f6654630e0f6f18497475b34e607f63a79f09140","modified":1631633856066},{"_id":"public/img/articles2.png","hash":"44f2e90e928bf1ae3fd91a85b70b5b1d07a91519","modified":1631633856066},{"_id":"public/img/avatar1.jpg","hash":"466d1b7cd0573e57e7db294c5f63c55ee6ca4296","modified":1631633856066},{"_id":"public/img/avatar2.jpg","hash":"4a7143ede9561ace432c2ac963b406428b296756","modified":1631633856066},{"_id":"public/img/favicon.png","hash":"3cf89864b4f6c9b532522a4d260a2e887971c92d","modified":1631633856066},{"_id":"public/img/friend_404.gif","hash":"8d2d0ebef70a8eb07329f57e645889b0e420fa48","modified":1631633856066},{"_id":"public/img/huiji.png","hash":"f33b6927a0124ce7ad10638b96ee3be9b55c555e","modified":1631633856066},{"_id":"public/img/mybatis.png","hash":"1e685804796630baba037812e3beab62936e1227","modified":1631633856066},{"_id":"public/img/mybatis1.png","hash":"d40a9abf2799be2e830868845e7287e884d8688d","modified":1631633856066},{"_id":"public/img/pcbimg.png","hash":"72afcf1951d00c206d235facf6e09d16802c6ba1","modified":1631633856066},{"_id":"public/img/pcbimg2.png","hash":"511c4a15ad0f382adcbd3e1ff86dca3ead5d2df1","modified":1631633856066},{"_id":"public/img/plane.png","hash":"fcd0b34a638838dcae8cbf17f34c51f973a0c02b","modified":1631633856066},{"_id":"public/img/plane1.png","hash":"c5713f3d92ddcd45d2b9a494f78b0f5b0e4a7466","modified":1631633856066},{"_id":"public/live2d-widget/LICENSE","hash":"1eba7caf09a39110ad2f542e3ed8700d1a69c6d3","modified":1631633856066},{"_id":"public/mouse/AppStarting.ani","hash":"e334f5489e63167adbd44f1116ffcfc72e84f718","modified":1631633856066},{"_id":"public/mouse/Arrow.cur","hash":"1ab6471c728291cf66ba813b09135d48a06e74c3","modified":1631633856066},{"_id":"public/mouse/Cross.cur","hash":"6d6d6f975164d58db643a84ed238375fcbdac98d","modified":1631633856066},{"_id":"public/mouse/Hand.cur","hash":"46d6d4e0a8a79e054df35bcee1a53bb3c4b1ec8b","modified":1631633856066},{"_id":"public/mouse/Handwriting.cur","hash":"195c938abc7f3e6d1f2e6301c46ba1e42ff99a4f","modified":1631633856066},{"_id":"public/mouse/Help.cur","hash":"bf59506b7a65e2a5bb8c07b9b3c8daa6b90b0b7d","modified":1631633856066},{"_id":"public/mouse/IBeam.cur","hash":"c96a55e806892deb9c1866c1aa33f98627d6dadf","modified":1631633856066},{"_id":"public/mouse/SizeAll.cur","hash":"b8ccca4354a8c11d61b7281818004933bdc32e01","modified":1631633856066},{"_id":"public/mouse/NO.cur","hash":"709b03659cd3be203e9d1c449d675e866847b957","modified":1631633856066},{"_id":"public/mouse/SizeNWSE.cur","hash":"25666d52fb83373286dc0b815387be41a52a26ac","modified":1631633856066},{"_id":"public/mouse/SizeNESW.cur","hash":"253f4e4c0da9b85757596c09187a79204fd9f07b","modified":1631633856066},{"_id":"public/mouse/SizeWE.cur","hash":"915ae8029726e0cf7b8f523fff059a96148cfc5c","modified":1631633856066},{"_id":"public/mouse/UpArrow.cur","hash":"2e65c3437b457ddf6429a096d5921ea962703843","modified":1631633856066},{"_id":"public/mouse/SizeNS.cur","hash":"4c02aa53675e758f61de6e13a5cf764b41e129f5","modified":1631633856066},{"_id":"public/live2d-widget/assets/screenshot-1.png","hash":"4bd4d97a1fcaa5deb3cea1c0a102a895a15af32f","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearchLite.min.js","hash":"284416885e4e80e27fa4eae6fc305f4de15b914c","modified":1631633856066},{"_id":"public/fonts/JetBrainsMono-Medium.woff2","hash":"f31bdf436d68c69503e96e7ca691a90ffa4918a8","modified":1631633856066},{"_id":"public/mouse/Wait.ani","hash":"5a7278ded553b61a248ec549b9964b7e2930cecf","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearchLite.js","hash":"e56ad6b82caf69066de545201014291fc961635e","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearch.min.js","hash":"a3b131a9a47ccc16f4dd8988fabb6d306548db2f","modified":1631633856066},{"_id":"public/css/copyright.css","hash":"45bc0d0d4889daabae8b6e0c129617fcc212b77b","modified":1631633856066},{"_id":"public/css/background.css","hash":"573aeea20ddcc77fa2b835ebba66e40b90635fb3","modified":1631633856066},{"_id":"public/css/custom.css","hash":"43a20c653bba7892c704bf7654d60cfdcfe4b40d","modified":1631633856066},{"_id":"public/css/icon.css","hash":"d93c1e972e3569c9a08fedf3f54a1fe28f2b036f","modified":1631633856066},{"_id":"public/css/mouse.css","hash":"72d8fe42bc9b8896dabcbd163323841d6ee565a8","modified":1631633856066},{"_id":"public/css/side.css","hash":"770be1a4d7eed917770462cc8d361ead355985c5","modified":1631633856066},{"_id":"public/css/var.css","hash":"da39a3ee5e6b4b0d3255bfef95601890afd80709","modified":1631633856066},{"_id":"public/js/utils.js","hash":"8319b59c26ce8cd2b0ae7d030c4912215148fa92","modified":1631633856066},{"_id":"public/live2d-widget/README.html","hash":"f4d5ae45aedcc8a1e6661dd7db64ca241c3758a5","modified":1631633856066},{"_id":"public/live2d-widget/autoload.js","hash":"e6540c988c9c2d6f57fabe4073469042fd4e088c","modified":1631633856066},{"_id":"public/live2d-widget/waifu-tips.js","hash":"f8bdc91fa1e51d46dc18e04174c63034d68299bf","modified":1631633856066},{"_id":"public/live2d-widget/waifu-tips.json","hash":"a3f9d4d832cd0948cd21385e7e8c62db377ba7d4","modified":1631633856066},{"_id":"public/live2d-widget/waifu.css","hash":"0613696628c20499c6f72bcec6b6780594443b0d","modified":1631633856066},{"_id":"public/js/search/algolia.js","hash":"65b45e61586f7e66c3f338370bfd9daadd71a4b7","modified":1631633856066},{"_id":"public/js/search/local-search.js","hash":"459e2541afda483916d16fce4aaa56b41bcd42ba","modified":1631633856066},{"_id":"public/live2d-widget/demo/login.html","hash":"9512df47f6516c0013153095693cba1992744f4b","modified":1631633856066},{"_id":"public/live2d-widget/demo/demo.html","hash":"2617b8aa4c4728d38a8f062465304e5433056abc","modified":1631633856066},{"_id":"public/css/index.css","hash":"09267c4348aec68b0104ec3ec2abb3fb5f1d067c","modified":1631633856066},{"_id":"public/live2d-widget/assets/screenshot-3.png","hash":"f89c6fdf44008e7d3c7a1aafe65dc39b212edb6c","modified":1631633856066},{"_id":"public/js/tw_cn.js","hash":"00053ce73210274b3679f42607edef1206eebc68","modified":1631633856066},{"_id":"public/js/main.js","hash":"8ef2821ceb92d81aa0f8c02ee932f094adcafb2b","modified":1631633856066},{"_id":"public/assets/algolia/algoliasearch.js","hash":"6948fcdf071e4983e784e8c458cf201536f77792","modified":1631633856066},{"_id":"public/css/index.min.css","hash":"fbc05a25b9ffcb535debd43ba244c96e909498c1","modified":1631633856066},{"_id":"public/live2d-widget/assets/screenshot-2.png","hash":"76b844653136184c573b3632e12296e008a7ff5c","modified":1631633856066},{"_id":"public/img/02.jpg","hash":"52276fbc7427eaf3facebf1e7cf9d99dad1664e8","modified":1631633856066},{"_id":"public/img/banner.png","hash":"e0b6d5d65d72bdfeb1b1bcfd1af340088d50fdfb","modified":1631633856066},{"_id":"public/img/01.jpg","hash":"455aff642579a632713b90c58edcd89f1ea01aef","modified":1631633856066},{"_id":"public/img/34.jpg","hash":"10b2ddfbe7f03c4513ace7a2855a47f907478d55","modified":1631633856066},{"_id":"public/img/blissed01.jpg","hash":"932db4b0cb8a297a4f9c1fd48af59183db742584","modified":1631633856066},{"_id":"public/live2d-widget/live2d.min.js","hash":"94383fb61de815c3f806ae3f11026bbcfe384082","modified":1631633856066},{"_id":"public/img/categories.png","hash":"1829d3667c9fb36d729057a2fb26f12b662c86e4","modified":1631633856066},{"_id":"public/fonts/loving.ttf","hash":"786ab7558a8a2cd5de1ac71a1c6c19bfeff17e95","modified":1631633856066},{"_id":"public/fonts/shishangblack.ttf","hash":"b5dda0beb2618829e41553d0dc656afbc78af523","modified":1631633856066},{"_id":"public/fonts/ajLangMan.ttf","hash":"c79e63ca78049f8273c0de7b5c65898c1dd8557f","modified":1631633856066},{"_id":"public/fonts/ZhuZiAWan.woff2","hash":"a8512c6d5d70f5fc098a58a01a0ad720aa211c2a","modified":1631633856066},{"_id":"source/_posts/算法/质数.md","hash":"07ce98a68be2dc524119a3fd57a930e53e9ab868","modified":1631845511156}],"Category":[{"name":"硬件学习","_id":"cktk8o6o10004akve0nho022p"},{"name":"51单片机","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6oe000sakvef4qjae8o"},{"name":"Hexo","_id":"cktk8o6ov001sakve83h63fm7"},{"name":"java","_id":"cktk8o6pc002lakve2iyjem8n"},{"name":"spring","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qe0055akvegob69vva"},{"name":"java面试","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qh005bakveacjzeuio"},{"name":"Maven","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qs0068akve66aj2w3e"},{"name":"Mybatis","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r30076akve2siqh8cu"},{"name":"MySQL","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r5007eakve6g5i7k0f"},{"name":"esp8266","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rl008wakveg62hemot"},{"name":"物联网开发","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rz00a5akvehafeesla"},{"name":"springMVC","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6s900beakvegzr10ttv"},{"name":"javaEE","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sj00cnakvedfi1cpk5"},{"name":"javaSE","parent":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sq00dhakve6e73glbu"},{"name":"win10","_id":"cktk8o6td00fzakve55x24m4x"},{"name":"stm32","parent":"cktk8o6o10004akve0nho022p","_id":"cktk8o6te00g7akvedfi64j0n"},{"name":"杂七杂八","_id":"cktk8o6th00gkakve2e1eb0kc"},{"name":"电磁场","_id":"cktk8o6th00goakve1pju20c4"},{"name":"网络通信","_id":"cktk8o6th00grakve7iws2msc"},{"name":"网络安全","_id":"cktk8o6ti00gtakve51ks4u05"},{"name":"矢量","parent":"cktk8o6th00goakve1pju20c4","_id":"cktk8o6ti00gwakve0x2ufj97"},{"name":"算法","_id":"cktnqr7by000184vecgng4l09"},{"name":"数学知识","parent":"cktnqr7by000184vecgng4l09","_id":"cktnqr7fd000484ve3lueg94u"}],"Data":[{"_id":"link","data":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}],"Page":[{"_content":"{\n \"name\": \"string\",\n \"short_name\": \"Junzhou\",\n \"theme_color\": \"#49b1f5\",\n \"background_color\": \"#49b1f5\",\n \"display\": \"standalone\",\n \"scope\": \"/\",\n \"start_url\": \"/\",\n \"icons\": [\n {\n \"src\": \"images/pwaicons/36.png\",\n \"sizes\": \"36x36\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/48.png\",\n \"sizes\": \"48x48\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/72.png\",\n \"sizes\": \"72x72\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/96.png\",\n \"sizes\": \"96x96\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/144.png\",\n \"sizes\": \"144x144\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/192.png\",\n \"sizes\": \"192x192\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/512.png\",\n \"sizes\": \"512x512\",\n \"type\": \"image/png\"\n }\n ],\n \"splash_pages\": null\n }","source":"manifest.json","raw":"{\n \"name\": \"string\",\n \"short_name\": \"Junzhou\",\n \"theme_color\": \"#49b1f5\",\n \"background_color\": \"#49b1f5\",\n \"display\": \"standalone\",\n \"scope\": \"/\",\n \"start_url\": \"/\",\n \"icons\": [\n {\n \"src\": \"images/pwaicons/36.png\",\n \"sizes\": \"36x36\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/48.png\",\n \"sizes\": \"48x48\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/72.png\",\n \"sizes\": \"72x72\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/96.png\",\n \"sizes\": \"96x96\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/144.png\",\n \"sizes\": \"144x144\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/192.png\",\n \"sizes\": \"192x192\",\n \"type\": \"image/png\"\n },\n {\n \"src\": \"images/pwaicons/512.png\",\n \"sizes\": \"512x512\",\n \"type\": \"image/png\"\n }\n ],\n \"splash_pages\": null\n }","date":"2021-09-14T09:24:37.372Z","updated":"2021-09-14T09:24:37.371Z","path":"manifest.json","layout":"false","title":"","comments":1,"_id":"cktk8o6nt0000akvebb85cs9q","content":"{\"name\":\"string\",\"short_name\":\"Junzhou\",\"theme_color\":\"#49b1f5\",\"background_color\":\"#49b1f5\",\"display\":\"standalone\",\"scope\":\"/\",\"start_url\":\"/\",\"icons\":[{\"src\":\"images/pwaicons/36.png\",\"sizes\":\"36x36\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/48.png\",\"sizes\":\"48x48\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/72.png\",\"sizes\":\"72x72\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/96.png\",\"sizes\":\"96x96\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/144.png\",\"sizes\":\"144x144\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/512.png\",\"sizes\":\"512x512\",\"type\":\"image/png\"}],\"splash_pages\":null}","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"{\"name\":\"string\",\"short_name\":\"Junzhou\",\"theme_color\":\"#49b1f5\",\"background_color\":\"#49b1f5\",\"display\":\"standalone\",\"scope\":\"/\",\"start_url\":\"/\",\"icons\":[{\"src\":\"images/pwaicons/36.png\",\"sizes\":\"36x36\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/48.png\",\"sizes\":\"48x48\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/72.png\",\"sizes\":\"72x72\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/96.png\",\"sizes\":\"96x96\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/144.png\",\"sizes\":\"144x144\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"images/pwaicons/512.png\",\"sizes\":\"512x512\",\"type\":\"image/png\"}],\"splash_pages\":null}"},{"title":"关于","date":"2021-08-20T14:32:50.000Z","type":"about","_content":"\n2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。\n\n\n\n嗯。。。。。。。\n就这样!!!!!","source":"about/index.md","raw":"---\ntitle: 关于\ndate: 2021-08-20 22:32:50\ntype: \"about\"\n---\n\n2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。\n\n\n\n嗯。。。。。。。\n就这样!!!!!","updated":"2021-08-20T14:37:08.214Z","path":"about/index.html","comments":1,"layout":"page","_id":"cktk8o6ny0002akve6dacfapw","content":"

2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。

\n

嗯。。。。。。。
就这样!!!!!

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"

2021暑假利用Hexo框架,使用butterfly主题,参考网上大神们做的个人博客,用来做笔记。。。

\n

嗯。。。。。。。
就这样!!!!!

\n"},{"title":"友情链接","date":"2021-08-17T05:12:28.000Z","type":"link","_content":"","source":"link/index.md","raw":"---\ntitle: 友情链接\ndate: 2021-08-17 13:12:28\ntype: link\n---\n","updated":"2021-08-17T05:12:52.461Z","path":"link/index.html","comments":1,"layout":"page","_id":"cktk8o6o20006akve6elihxpj","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":""},{"title":"音乐","date":"2021-08-17T13:17:27.000Z","type":"music","_content":"\n\n{% tabs 音乐, -1 %}\n\n[网易云音乐-我的音乐](https://music.163.com/#/my/m/music/playlist?id=5322139721)\n\n{% folding 歌单1 %}\n{% meting \"6925236447\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-09 %}\n{% meting \"6952136420\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-08 %}\n{% meting \"6916454908\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-06 %}\n{% meting \"6826165381\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-05 %}\n{% meting \"6749189366\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-04[2] %}\n{% meting \"6718667049\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-04[1] %}\n{% meting \"6678848086\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-03%}\n{% meting \"6649685822\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷狗音乐](https://www.kugou.com/)\n\n{% folding 大杂烩%}\n{% meting \"37118256\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"38928927\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"12869806\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[QQ音乐](https://y.qq.com/)\n\nQQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是\n\n{% folding 歌单1%}\n{% meting \"8133710813\" \"tencent\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷我音乐](http://www.kuwo.cn/)\n\n{% folding 展开%}\n\n\n\n{% endfolding %}\n\n\n\n\n\n\n芳华慢-封茗囧菌\n{% audio https://webfs.ali.kugou.com/202109120929/fdccfb42189820d5c6cac2e9ec9f7e8b/KGTX/CLTX001/4e38a814fe40ae680ab67ced203487a5.mp3 %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n","source":"music/index.md","raw":"---\ntitle: 音乐\ndate: 2021-08-17 21:17:27\ntype: \"music\"\n---\n\n\n{% tabs 音乐, -1 %}\n\n[网易云音乐-我的音乐](https://music.163.com/#/my/m/music/playlist?id=5322139721)\n\n{% folding 歌单1 %}\n{% meting \"6925236447\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-09 %}\n{% meting \"6952136420\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n{% folding 歌单2021-08 %}\n{% meting \"6916454908\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-06 %}\n{% meting \"6826165381\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-05 %}\n{% meting \"6749189366\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-04[2] %}\n{% meting \"6718667049\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n{% folding 歌单2021-04[1] %}\n{% meting \"6678848086\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n{% folding 歌单2021-03%}\n{% meting \"6649685822\" \"netease\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷狗音乐](https://www.kugou.com/)\n\n{% folding 大杂烩%}\n{% meting \"37118256\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"38928927\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" %}\n{% meting \"12869806\" \"kugou\" \"album\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[QQ音乐](https://y.qq.com/)\n\nQQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是\n\n{% folding 歌单1%}\n{% meting \"8133710813\" \"tencent\" \"playlist\" \"theme:#3F51B5\" \"mutex:true\" \"preload:auto\" %}\n{% endfolding %}\n\n\n\n\n[酷我音乐](http://www.kuwo.cn/)\n\n{% folding 展开%}\n\n\n\n{% endfolding %}\n\n\n\n\n\n\n芳华慢-封茗囧菌\n{% audio https://webfs.ali.kugou.com/202109120929/fdccfb42189820d5c6cac2e9ec9f7e8b/KGTX/CLTX001/4e38a814fe40ae680ab67ced203487a5.mp3 %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n","updated":"2021-09-12T08:52:58.221Z","path":"music/index.html","comments":1,"layout":"page","_id":"cktk8o6o40008akve8p3pgh18","content":"

网易云音乐-我的音乐

\n
歌单1 \n
\n
\n
\n
\n\n
歌单2021-09 \n
\n
\n
\n
\n\n
歌单2021-08 \n
\n
\n
\n
\n\n\n
歌单2021-06 \n
\n
\n
\n
\n\n\n\n
歌单2021-05 \n
\n
\n
\n
\n\n\n\n
歌单2021-04[2] \n
\n
\n
\n
\n\n\n
歌单2021-04[1] \n
\n
\n
\n
\n\n\n\n
歌单2021-03 \n
\n
\n
\n

酷狗音乐

\n
大杂烩 \n
\n
\n
\n

QQ音乐

\n

QQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是

\n
歌单1 \n
\n
\n
\n

酷我音乐
<!—
<div id=”aplayer-jyZgFIvs” class=”aplayer aplayer-tag-marker meting-tag-marker”
data-id=”72510362” data-server=”kuwo” data-type=”yinyue” data-mode=”circulation” data-autoplay=”false” data-mutex=”true” data-listmaxheight=”340px” data-preload=”auto” data-theme=”#3F51B5”

\n
></div> -->\n
展开 \n
\n \n
\n

芳华慢-封茗囧菌

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

网易云音乐-我的音乐

\n
歌单1 \n
\n
\n
\n
\n\n
歌单2021-09 \n
\n
\n
\n
\n\n
歌单2021-08 \n
\n
\n
\n
\n\n\n
歌单2021-06 \n
\n
\n
\n
\n\n\n\n
歌单2021-05 \n
\n
\n
\n
\n\n\n\n
歌单2021-04[2] \n
\n
\n
\n
\n\n\n
歌单2021-04[1] \n
\n
\n
\n
\n\n\n\n
歌单2021-03 \n
\n
\n
\n

酷狗音乐

\n
大杂烩 \n
\n
\n
\n

QQ音乐

\n

QQ音乐的音乐id获取:点击分享歌曲获取链接,在链接中的songid就是

\n
歌单1 \n
\n
\n
\n

酷我音乐
<!—
<div id=”aplayer-jyZgFIvs” class=”aplayer aplayer-tag-marker meting-tag-marker”
data-id=”72510362” data-server=”kuwo” data-type=”yinyue” data-mode=”circulation” data-autoplay=”false” data-mutex=”true” data-listmaxheight=”340px” data-preload=”auto” data-theme=”#3F51B5”

\n
></div> -->\n
展开 \n
\n \n
\n

芳华慢-封茗囧菌

\n
\n"},{"title":"分类","date":"2021-08-17T05:10:35.000Z","type":"categories","_content":"","source":"categories/index.md","raw":"---\ntitle: 分类\ndate: 2021-08-17 13:10:35\ntype: \"categories\"\n# top_img: false\n---\n","updated":"2021-08-17T11:26:53.838Z","path":"categories/index.html","comments":1,"layout":"page","_id":"cktk8o6o5000aakve8jlidwd2","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":""},{"title":"笔记","date":"2021-09-01T00:27:26.000Z","type":"notes","_content":"","source":"notes/index.md","raw":"---\ntitle: 笔记\ndate: 2021-09-01 08:27:26\ntype: \"notes\"\n---\n","updated":"2021-09-01T00:28:37.545Z","path":"notes/index.html","comments":1,"layout":"page","_id":"cktk8o6o9000fakveeaeogkyt","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":""},{"title":"标签","date":"2021-08-17T05:11:25.000Z","type":"tags","_content":"","source":"tags/index.md","raw":"---\ntitle: 标签\ndate: 2021-08-17 13:11:25\ntype: \"tags\"\n---\n","updated":"2021-08-17T05:11:46.636Z","path":"tags/index.html","comments":1,"layout":"page","_id":"cktk8o6oa000iakve6bb9guiq","content":"","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":""},{"title":"诗歌","date":"2021-09-01T05:19:34.000Z","type":"poems","_content":"\n{% folding blue, 水调歌头 %}\n{% poem 水调歌头,苏轼 %}\n丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。\n明月几时有?把酒问青天。\n不知天上宫阙,今夕是何年?\n我欲乘风归去,又恐琼楼玉宇,高处不胜寒。\n起舞弄清影,何似在人间?\n\n转朱阁,低绮户,照无眠。\n不应有恨,何事长向别时圆?\n人有悲欢离合,月有阴晴圆缺,此事古难全。\n但愿人长久,千里共婵娟。\n{% endpoem %}\n{%endfolding%}\n\n{% folding blue, 古诗十九首 %}\n{% poem 迢迢牵牛星 %}\n迢迢牵牛星,皎皎河汉女。\n纤纤擢素手,札札弄机杼。\n终日不成章,泣涕零如雨。\n河汉清且浅,相去复几许?\n盈盈一水间,脉脉不得语。\n{% endpoem %}\n\n{% poem 行行重行行 %}\n行行重行行,与君生别离。\n相去万余里,各在天一涯。\n道路阻且长,会面安可知?\n胡马依北风,越鸟巢南枝。\n相去日已远,衣带日已缓。\n浮云蔽白日,游子不顾反。\n思君令人老,岁月忽已晚。\n弃捐勿复道,努力加餐饭\n{% endpoem %}\n\n\n{%endfolding%}\n\n","source":"poems/index.md","raw":"---\ntitle: 诗歌\ndate: 2021-09-01 13:19:34\ntype: \"poems\"\n---\n\n{% folding blue, 水调歌头 %}\n{% poem 水调歌头,苏轼 %}\n丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。\n明月几时有?把酒问青天。\n不知天上宫阙,今夕是何年?\n我欲乘风归去,又恐琼楼玉宇,高处不胜寒。\n起舞弄清影,何似在人间?\n\n转朱阁,低绮户,照无眠。\n不应有恨,何事长向别时圆?\n人有悲欢离合,月有阴晴圆缺,此事古难全。\n但愿人长久,千里共婵娟。\n{% endpoem %}\n{%endfolding%}\n\n{% folding blue, 古诗十九首 %}\n{% poem 迢迢牵牛星 %}\n迢迢牵牛星,皎皎河汉女。\n纤纤擢素手,札札弄机杼。\n终日不成章,泣涕零如雨。\n河汉清且浅,相去复几许?\n盈盈一水间,脉脉不得语。\n{% endpoem %}\n\n{% poem 行行重行行 %}\n行行重行行,与君生别离。\n相去万余里,各在天一涯。\n道路阻且长,会面安可知?\n胡马依北风,越鸟巢南枝。\n相去日已远,衣带日已缓。\n浮云蔽白日,游子不顾反。\n思君令人老,岁月忽已晚。\n弃捐勿复道,努力加餐饭\n{% endpoem %}\n\n\n{%endfolding%}\n\n","updated":"2021-09-01T11:21:57.769Z","path":"poems/index.html","comments":1,"layout":"page","_id":"cktk8o6oc000nakve8s3d8v6m","content":"
水调歌头 \n
\n
水调歌头
苏轼

丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。
明月几时有?把酒问青天。
不知天上宫阙,今夕是何年?
我欲乘风归去,又恐琼楼玉宇,高处不胜寒。
起舞弄清影,何似在人间?

转朱阁,低绮户,照无眠。
不应有恨,何事长向别时圆?
人有悲欢离合,月有阴晴圆缺,此事古难全。
但愿人长久,千里共婵娟。

\n
\n
\n
古诗十九首 \n
\n
迢迢牵牛星

迢迢牵牛星,皎皎河汉女。
纤纤擢素手,札札弄机杼。
终日不成章,泣涕零如雨。
河汉清且浅,相去复几许?
盈盈一水间,脉脉不得语。

行行重行行

行行重行行,与君生别离。
相去万余里,各在天一涯。
道路阻且长,会面安可知?
胡马依北风,越鸟巢南枝。
相去日已远,衣带日已缓。
浮云蔽白日,游子不顾反。
思君令人老,岁月忽已晚。
弃捐勿复道,努力加餐饭

\n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"
水调歌头 \n
\n
水调歌头
苏轼

丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。
明月几时有?把酒问青天。
不知天上宫阙,今夕是何年?
我欲乘风归去,又恐琼楼玉宇,高处不胜寒。
起舞弄清影,何似在人间?

转朱阁,低绮户,照无眠。
不应有恨,何事长向别时圆?
人有悲欢离合,月有阴晴圆缺,此事古难全。
但愿人长久,千里共婵娟。

\n
\n
\n
古诗十九首 \n
\n
迢迢牵牛星

迢迢牵牛星,皎皎河汉女。
纤纤擢素手,札札弄机杼。
终日不成章,泣涕零如雨。
河汉清且浅,相去复几许?
盈盈一水间,脉脉不得语。

行行重行行

行行重行行,与君生别离。
相去万余里,各在天一涯。
道路阻且长,会面安可知?
胡马依北风,越鸟巢南枝。
相去日已远,衣带日已缓。
浮云蔽白日,游子不顾反。
思君令人老,岁月忽已晚。
弃捐勿复道,努力加餐饭

\n
\n
\n"},{"title":"网站链接","date":"2021-09-01T01:38:54.000Z","type":"web_links","_content":"\n\n{% folding blue, 工具网站 %}\n\n\n\n\n1. [百度脑图-便捷的思维工具](https://naotu.baidu.com/home)\n2. [【GitMind官网】- 免费在线思维导图软件](https://gitmind.cn/)\n3. [程序员工具 - 聚够网](http://tool.ggo.net/)\n4. [程序员常用工具箱 -TOOLFK](https://www.toolfk.com/)\n5. [json解析](http://sojson.com/)\n6. [微词云 - 制作 - 设计页](https://www.weiciyun.com/u/create#/?k=kecplc6u)\n7. [在线生成透明ICO图标——ICO图标制作](http://www.ico51.cn/)\n8. [math-latex](https://webdemo.myscript.com/views/math/index.html#)\n9. [LaTeX在线:吴文中数学公式编辑器](https://latex.91maths.com/)\n10. [638+ 演示文稿模板 - 模板素材库 - Canva可画](https://www.canva.cn/templates/search/5ryU56S65paH56i_X64/)\n11. [ppt模板 - 学堂PPT](https://xtppt.cn/PPTMB/YQPPT/1792.html)\n12. [JetBrains](https://www.jetbrains.com/)\n13. [GitHub 下载文件加速](https://gh.api.99988866.xyz/)\n14. [博客文章图片生成](https://nav.rdonly.com/laboratory/bgimage/backimage.html)\n15. [徽标生成工具](https://shields.io/)\n\n\n{% endfolding %}","source":"web-links/index.md","raw":"---\ntitle: 网站链接\ndate: 2021-09-01 09:38:54\ntype: \"web_links\"\n---\n\n\n{% folding blue, 工具网站 %}\n\n\n\n\n1. [百度脑图-便捷的思维工具](https://naotu.baidu.com/home)\n2. [【GitMind官网】- 免费在线思维导图软件](https://gitmind.cn/)\n3. [程序员工具 - 聚够网](http://tool.ggo.net/)\n4. [程序员常用工具箱 -TOOLFK](https://www.toolfk.com/)\n5. [json解析](http://sojson.com/)\n6. [微词云 - 制作 - 设计页](https://www.weiciyun.com/u/create#/?k=kecplc6u)\n7. [在线生成透明ICO图标——ICO图标制作](http://www.ico51.cn/)\n8. [math-latex](https://webdemo.myscript.com/views/math/index.html#)\n9. [LaTeX在线:吴文中数学公式编辑器](https://latex.91maths.com/)\n10. [638+ 演示文稿模板 - 模板素材库 - Canva可画](https://www.canva.cn/templates/search/5ryU56S65paH56i_X64/)\n11. [ppt模板 - 学堂PPT](https://xtppt.cn/PPTMB/YQPPT/1792.html)\n12. [JetBrains](https://www.jetbrains.com/)\n13. [GitHub 下载文件加速](https://gh.api.99988866.xyz/)\n14. [博客文章图片生成](https://nav.rdonly.com/laboratory/bgimage/backimage.html)\n15. [徽标生成工具](https://shields.io/)\n\n\n{% endfolding %}","updated":"2021-09-01T02:05:12.181Z","path":"web-links/index.html","comments":1,"layout":"page","_id":"cktk8o6od000qakveak0t3zck","content":"
工具网站 \n \n
","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"
工具网站 \n \n
"}],"Post":[{"title":"51单片机学习笔记(一)","description":"点亮第一个led灯、流水灯、蜂鸣器、数码管、按键","abbrlink":"9e0a8624","date":"2021-08-17T16:00:00.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n## 点亮第一个led灯\n\n\n\n1. 新建工程\n\n\t![image-20210821203338653](https://gitee.com/ajream/images/raw/master/img/20210821203342_image-20210821203338653.png)\n\n2. 选择固件\n\n\t![image-20210821203543242](https://gitee.com/ajream/images/raw/master/img/20210821203544_image-20210821203543242.png)\n\n\t![image-20210821203717166](https://gitee.com/ajream/images/raw/master/img/20210821203718_image-20210821203717166.png)\n\n3. 新建代码源文件\n\n\t![image-20210821203932209](https://gitee.com/ajream/images/raw/master/img/20210821203933_image-20210821203932209.png)\n\n\t![image-20210821204130367](https://gitee.com/ajream/images/raw/master/img/20210821204131_image-20210821204130367.png)\n\n\t![image-20210821204227781](https://gitee.com/ajream/images/raw/master/img/20210821204229_image-20210821204227781.png)\n\n4. 添加如下代码\n\n\t```c\n\t#include //引用51头文件\n\t\n\tsbit LED = P1^1;\n\t\n\tvoid main()\n\t{\n\t\tLED = 0;\t//点亮LED2\t\n\t}\n\t```\n5. 编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面\n\n ![image-20210821204723747](https://gitee.com/ajream/images/raw/master/img/20210821204724_image-20210821204723747.png)\n6. 编译\n\n ![image-20210821205109242](https://gitee.com/ajream/images/raw/master/img/20210821205110_image-20210821205109242.png)\n\n7. 上传(烧录)\n\n ![image-20210821205434569](https://gitee.com/ajream/images/raw/master/img/20210821205435_image-20210821205434569.png)\n\n\n\n到此第一个点灯小程序结束\n\n\n\n\n\n## 流水灯\n\n\n\n按照之前的步骤再建立一个新工程,写下如下代码:\n\n```c\n#include \t //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nuchar temp; //LED灯相关变量\n\n//延时函数\nvoid delay(uint z)\n{\n\tuint x, y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\ttemp = 0xfe;\n\tP1 = temp; //1111 1110 初值LED1亮\n\tdelay(1000);//毫秒级延时 100毫秒\n\twhile(1)\n\t{\n\t\ttemp = _cror_(temp, 1);//循环左移\n\t\tP1 = temp;//移位完成后赋值给P1 每个一个灯点亮\n\t\tdelay(1000);//毫秒级延时 100毫秒\n\t}\t\n} \n```\n\n说明:\n\n- `intrins.h` 头文件包含移位函数 :`右移_cror_()`、 `左移_corl_()`\n\n\n\n## 蜂鸣器\n\n蜂鸣器分有源与无源蜂鸣器\n\n- 有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音\n- 无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响\n\n一个蜂鸣器的简单驱动电路:\n\n![image-20210821210804064](https://gitee.com/ajream/images/raw/master/img/20210821210805_image-20210821210804064.png)\n\n```c\n#include \t//包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n\nsbit beep = P2^3;//蜂鸣器\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\twhile(1)\n\t{\n\t\tbeep = ~beep;//\t蜂鸣器发出滴滴声\n\t\tdelay(100);\n\t}\t\n} \n```\n\n\n\n\n\n## 数码管\n\n\n\n### 原理\n\n单个数码管结构原理:\n\n![image-20210821211542277](https://gitee.com/ajream/images/raw/master/img/20210821211543_image-20210821211542277.png)\n\n\n\n多个数码管:\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n说明:\n\n74HC573是个锁存器,原理如下:\n\n![image-20210821212211630](https://gitee.com/ajream/images/raw/master/img/20210821212212_image-20210821212211630.png)\n\n当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化\n\n当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态\n\n\n\n在这幅图\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n\n\n用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符\n\n在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即`1111 1110`),最左边这个数码管就被选中了。\n\n数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下\n\n```c\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F\n// 0 1 2 3\t4\t 5\t 6 7 8 9 \n0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x40, 0x00\n//A B C D E F H L - 熄灭\n```\n\n\n\n### 数码管静态显示\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管位选\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110 选通最左边的数码管\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\tP0 = 0X5b;//1101 1011 显示“1”\n\tDU = 0;//锁存段选数据\n\t\n\twhile(1);\n} \n```\n\n\n\n### 数码管动态显示\n\n要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n*8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即**快速扫描**每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示\n\n\n\n这个代码用来控制3个数码管来显示一个三位数\n\n```c\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n//设计一个函数用于显示三位的数字number\nvoid display(uchar number)\n{\n\tuchar bai, shi, ge;\n\tbai = number / 100; \t\t//百位\n\tshi = number % 100 / 10;\t//十位\n\tge = number % 10;\t\t\t//个位\n\t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\nvoid main()//main函数自身会循环\n{\t\n\twhile(1)\n\t{\n\t\tdisplay(185); //数码管显示185\n\t}\t\n} \n```\n\n\n\n\n\n\n\n## 键盘\n\n非编码键盘分为独立键盘和矩阵键盘\n\n### 独立键盘\n\n\n\n![image-20210821222715524](https://gitee.com/ajream/images/raw/master/img/20210821222718_image-20210821222715524.png)\n\n对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测\n\nP30是否为低电平即可\n\n> 【注意】\n>\n> 1. 按键消抖,最简单的是延时10~20ms后再检查其状态是否变化\n> 2. 松手检测\n\n```c\n/*********************************************************************************\n \t按下开发板S2按键数码管值+1,最大到9\n\t按下S3按下,值-1,最小减到0\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\n\n//毫秒级延时函数定义\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 9)//如果值不等于9则+1,功能把值限定为小于9\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\t//如果大于0则执行减一\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t\t//松手之后刷新显示\n\t\tDU = 1;//打开段选锁存器\n\t\tP0 = tabel[num];//\n\t\tDU = 0;//锁存段选数据\n\t}\t\n} \n```\n\n\n\n### 矩阵键盘\n\n判断矩阵按键中某个按键是否按下可采用**先列扫描再行扫描**的方式\n\n1. 列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了;\n2. 行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。\n\n\n\n```c\n/*********************************************************************************\n* 【程序功能】: \t4*4矩阵键盘与4位独立键盘识别\t\t \t\t\t \t\t\t \n* 【使用说明】: \t按下矩阵键盘和独立键盘任意键,数码管显示相应数值\n\t\t\t\t 初始显示“-”横\n**********************************************************************************/\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\nuchar KeyValue = 20;//按键值 显示-\n\n//共阴数码管段选表\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x37, 0x3E, 0x40, 0x00 }; \n//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,H,L,,n,u,-,熄灭\n\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid KeyScan()\n{\n\t//4*4矩阵键盘扫描\n\tP3 = 0XF0;//列扫描\n\tif(P3 != 0XF0)//判断按键是否被按下\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XF0)//判断按键是否被按下\n\t\t{\n\t\t\tswitch(P3) //判断那一列被按下\n\t\t\t{\n\t\t\t\tcase 0xe0:\tKeyValue = 0;\tbreak;//第一列被按下\n\t\t\t\tcase 0xd0:\tKeyValue = 1;\tbreak;//第二列被按下\n\t\t\t\tcase 0xb0:\tKeyValue = 2;\tbreak;//第三列被按下\n\t\t\t\tcase 0x70:\tKeyValue = 3;\tbreak;//第四列被按下 \n\t\t\t}\n\t\t\tP3 = 0X0F;//行扫描\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0x0e:\tKeyValue = KeyValue;\tbreak;//第一行被按下\n\t\t\t\tcase 0x0d:\tKeyValue = KeyValue + 4;\tbreak;//第二行被按下\n\t\t\t\tcase 0x0b:\tKeyValue = KeyValue + 8;\tbreak;//第三行被按下\n\t\t\t\tcase 0x07:\tKeyValue = KeyValue + 12;\tbreak;//第四行被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0X0F);//松手检测\t\n\t\t}\n\t}\n \n\tP3 = 0XFF;//独立按键扫描\n\tif(P3 != 0XFF)\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XFF)\n\t\t{\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0xfe:\tKeyValue = 16;\tbreak;//S2被按下\n\t\t\t\tcase 0xfd:\tKeyValue = 17;\tbreak;//S3被按下\n\t\t\t\tcase 0xfb:\tKeyValue = 18;\tbreak;//S4被按下\n\t\t\t\tcase 0xf7:\tKeyValue = 19;\tbreak;//S5被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0XFF);//松手检测\t\t\t\n\t\t}\t\n\t}\n\n}\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\twhile(1)\n\t{\n\t\tKeyScan();//20个按键键盘扫描\n\t\tP0 = tabel[KeyValue];//显示按键值\n\t}\t\n} \n```\n\n\n\n> 总结:先列扫描,再行扫描;或者先行扫描,再列扫描\n\n\n\n\n\n","source":"_posts/51单片机/51单片机学习笔记(一).md","raw":"---\ntitle: 51单片机学习笔记(一)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 点亮第一个led灯、流水灯、蜂鸣器、数码管、按键\nabbrlink: 9e0a8624\ndate: 2021-08-18 00:00:00\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n## 点亮第一个led灯\n\n\n\n1. 新建工程\n\n\t![image-20210821203338653](https://gitee.com/ajream/images/raw/master/img/20210821203342_image-20210821203338653.png)\n\n2. 选择固件\n\n\t![image-20210821203543242](https://gitee.com/ajream/images/raw/master/img/20210821203544_image-20210821203543242.png)\n\n\t![image-20210821203717166](https://gitee.com/ajream/images/raw/master/img/20210821203718_image-20210821203717166.png)\n\n3. 新建代码源文件\n\n\t![image-20210821203932209](https://gitee.com/ajream/images/raw/master/img/20210821203933_image-20210821203932209.png)\n\n\t![image-20210821204130367](https://gitee.com/ajream/images/raw/master/img/20210821204131_image-20210821204130367.png)\n\n\t![image-20210821204227781](https://gitee.com/ajream/images/raw/master/img/20210821204229_image-20210821204227781.png)\n\n4. 添加如下代码\n\n\t```c\n\t#include //引用51头文件\n\t\n\tsbit LED = P1^1;\n\t\n\tvoid main()\n\t{\n\t\tLED = 0;\t//点亮LED2\t\n\t}\n\t```\n5. 编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面\n\n ![image-20210821204723747](https://gitee.com/ajream/images/raw/master/img/20210821204724_image-20210821204723747.png)\n6. 编译\n\n ![image-20210821205109242](https://gitee.com/ajream/images/raw/master/img/20210821205110_image-20210821205109242.png)\n\n7. 上传(烧录)\n\n ![image-20210821205434569](https://gitee.com/ajream/images/raw/master/img/20210821205435_image-20210821205434569.png)\n\n\n\n到此第一个点灯小程序结束\n\n\n\n\n\n## 流水灯\n\n\n\n按照之前的步骤再建立一个新工程,写下如下代码:\n\n```c\n#include \t //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nuchar temp; //LED灯相关变量\n\n//延时函数\nvoid delay(uint z)\n{\n\tuint x, y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\ttemp = 0xfe;\n\tP1 = temp; //1111 1110 初值LED1亮\n\tdelay(1000);//毫秒级延时 100毫秒\n\twhile(1)\n\t{\n\t\ttemp = _cror_(temp, 1);//循环左移\n\t\tP1 = temp;//移位完成后赋值给P1 每个一个灯点亮\n\t\tdelay(1000);//毫秒级延时 100毫秒\n\t}\t\n} \n```\n\n说明:\n\n- `intrins.h` 头文件包含移位函数 :`右移_cror_()`、 `左移_corl_()`\n\n\n\n## 蜂鸣器\n\n蜂鸣器分有源与无源蜂鸣器\n\n- 有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音\n- 无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响\n\n一个蜂鸣器的简单驱动电路:\n\n![image-20210821210804064](https://gitee.com/ajream/images/raw/master/img/20210821210805_image-20210821210804064.png)\n\n```c\n#include \t//包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n\nsbit beep = P2^3;//蜂鸣器\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()\n{\n\twhile(1)\n\t{\n\t\tbeep = ~beep;//\t蜂鸣器发出滴滴声\n\t\tdelay(100);\n\t}\t\n} \n```\n\n\n\n\n\n## 数码管\n\n\n\n### 原理\n\n单个数码管结构原理:\n\n![image-20210821211542277](https://gitee.com/ajream/images/raw/master/img/20210821211543_image-20210821211542277.png)\n\n\n\n多个数码管:\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n说明:\n\n74HC573是个锁存器,原理如下:\n\n![image-20210821212211630](https://gitee.com/ajream/images/raw/master/img/20210821212212_image-20210821212211630.png)\n\n当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化\n\n当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态\n\n\n\n在这幅图\n\n![image-20210821211818619](https://gitee.com/ajream/images/raw/master/img/20210821211819_image-20210821211818619.png)\n\n\n\n用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符\n\n在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即`1111 1110`),最左边这个数码管就被选中了。\n\n数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下\n\n```c\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F\n// 0 1 2 3\t4\t 5\t 6 7 8 9 \n0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x40, 0x00\n//A B C D E F H L - 熄灭\n```\n\n\n\n### 数码管静态显示\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管位选\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110 选通最左边的数码管\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\tP0 = 0X5b;//1101 1011 显示“1”\n\tDU = 0;//锁存段选数据\n\t\n\twhile(1);\n} \n```\n\n\n\n### 数码管动态显示\n\n要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n*8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即**快速扫描**每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示\n\n\n\n这个代码用来控制3个数码管来显示一个三位数\n\n```c\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n//设计一个函数用于显示三位的数字number\nvoid display(uchar number)\n{\n\tuchar bai, shi, ge;\n\tbai = number / 100; \t\t//百位\n\tshi = number % 100 / 10;\t//十位\n\tge = number % 10;\t\t\t//个位\n\t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\nvoid main()//main函数自身会循环\n{\t\n\twhile(1)\n\t{\n\t\tdisplay(185); //数码管显示185\n\t}\t\n} \n```\n\n\n\n\n\n\n\n## 键盘\n\n非编码键盘分为独立键盘和矩阵键盘\n\n### 独立键盘\n\n\n\n![image-20210821222715524](https://gitee.com/ajream/images/raw/master/img/20210821222718_image-20210821222715524.png)\n\n对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测\n\nP30是否为低电平即可\n\n> 【注意】\n>\n> 1. 按键消抖,最简单的是延时10~20ms后再检查其状态是否变化\n> 2. 松手检测\n\n```c\n/*********************************************************************************\n \t按下开发板S2按键数码管值+1,最大到9\n\t按下S3按下,值-1,最小减到0\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\n\n//毫秒级延时函数定义\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 9)//如果值不等于9则+1,功能把值限定为小于9\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\t//如果大于0则执行减一\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t\t//松手之后刷新显示\n\t\tDU = 1;//打开段选锁存器\n\t\tP0 = tabel[num];//\n\t\tDU = 0;//锁存段选数据\n\t}\t\n} \n```\n\n\n\n### 矩阵键盘\n\n判断矩阵按键中某个按键是否按下可采用**先列扫描再行扫描**的方式\n\n1. 列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了;\n2. 行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。\n\n\n\n```c\n/*********************************************************************************\n* 【程序功能】: \t4*4矩阵键盘与4位独立键盘识别\t\t \t\t\t \t\t\t \n* 【使用说明】: \t按下矩阵键盘和独立键盘任意键,数码管显示相应数值\n\t\t\t\t 初始显示“-”横\n**********************************************************************************/\n#include //包含51头文件\n#include //包含移位标准库函数头文件\n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\nuchar KeyValue = 20;//按键值 显示-\n\n//共阴数码管段选表\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x37, 0x3E, 0x40, 0x00 }; \n//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,H,L,,n,u,-,熄灭\n\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid KeyScan()\n{\n\t//4*4矩阵键盘扫描\n\tP3 = 0XF0;//列扫描\n\tif(P3 != 0XF0)//判断按键是否被按下\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XF0)//判断按键是否被按下\n\t\t{\n\t\t\tswitch(P3) //判断那一列被按下\n\t\t\t{\n\t\t\t\tcase 0xe0:\tKeyValue = 0;\tbreak;//第一列被按下\n\t\t\t\tcase 0xd0:\tKeyValue = 1;\tbreak;//第二列被按下\n\t\t\t\tcase 0xb0:\tKeyValue = 2;\tbreak;//第三列被按下\n\t\t\t\tcase 0x70:\tKeyValue = 3;\tbreak;//第四列被按下 \n\t\t\t}\n\t\t\tP3 = 0X0F;//行扫描\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0x0e:\tKeyValue = KeyValue;\tbreak;//第一行被按下\n\t\t\t\tcase 0x0d:\tKeyValue = KeyValue + 4;\tbreak;//第二行被按下\n\t\t\t\tcase 0x0b:\tKeyValue = KeyValue + 8;\tbreak;//第三行被按下\n\t\t\t\tcase 0x07:\tKeyValue = KeyValue + 12;\tbreak;//第四行被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0X0F);//松手检测\t\n\t\t}\n\t}\n \n\tP3 = 0XFF;//独立按键扫描\n\tif(P3 != 0XFF)\n\t{\n\t\tdelay(10);//软件消抖10ms\n\t\tif(P3 != 0XFF)\n\t\t{\n\t\t\tswitch(P3) //判断那一行被按下\n\t\t\t{\n\t\t\t\tcase 0xfe:\tKeyValue = 16;\tbreak;//S2被按下\n\t\t\t\tcase 0xfd:\tKeyValue = 17;\tbreak;//S3被按下\n\t\t\t\tcase 0xfb:\tKeyValue = 18;\tbreak;//S4被按下\n\t\t\t\tcase 0xf7:\tKeyValue = 19;\tbreak;//S5被按下 \n\t\t\t}\n\t\t\twhile(P3 != 0XFF);//松手检测\t\t\t\n\t\t}\t\n\t}\n\n}\n\nvoid main()//main函数自身会循环\n{\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\n\tDU = 1;//打开段选锁存器\n\twhile(1)\n\t{\n\t\tKeyScan();//20个按键键盘扫描\n\t\tP0 = tabel[KeyValue];//显示按键值\n\t}\t\n} \n```\n\n\n\n> 总结:先列扫描,再行扫描;或者先行扫描,再列扫描\n\n\n\n\n\n","slug":"51单片机/51单片机学习笔记(一)","published":1,"updated":"2021-09-10T12:45:53.371Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6nw0001akve96rfdnwk","content":"

点亮第一个led灯

    \n
  1. 新建工程

    \n

    \"image-20210821203338653\"

    \n
  2. \n
  3. 选择固件

    \n

    \"image-20210821203543242\"

    \n

    \"image-20210821203717166\"

    \n
  4. \n
  5. 新建代码源文件

    \n

    \"image-20210821203932209\"

    \n

    \"image-20210821204130367\"

    \n

    \"image-20210821204227781\"

    \n
  6. \n
  7. 添加如下代码

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    #include <reg52.h> //引用51头文件

    sbit LED = P1^1;

    void main()
    {
    \tLED = 0;\t//点亮LED2\t
    }
  8. \n
  9. 编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面

    \n

    \"image-20210821204723747\"

    \n
  10. \n
  11. 编译

    \n

    \"image-20210821205109242\"

    \n
  12. \n
  13. 上传(烧录)

    \n

    \"image-20210821205434569\"

    \n
  14. \n
\n

到此第一个点灯小程序结束

\n

流水灯

按照之前的步骤再建立一个新工程,写下如下代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <reg52.h>\t //包含51头文件
#include <intrins.h> //包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

uchar temp; //LED灯相关变量

//延时函数
void delay(uint z)
{
\tuint x, y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()
{
\ttemp = 0xfe;
\tP1 = temp; //1111 1110 初值LED1亮
\tdelay(1000);//毫秒级延时 100毫秒
\twhile(1)
\t{
\t\ttemp = _cror_(temp, 1);//循环左移
\t\tP1 = temp;//移位完成后赋值给P1 每个一个灯点亮
\t\tdelay(1000);//毫秒级延时 100毫秒
\t}\t
}
\n

说明:

\n
    \n
  • intrins.h 头文件包含移位函数 :右移_cror_()左移_corl_()
  • \n
\n

蜂鸣器

蜂鸣器分有源与无源蜂鸣器

\n
    \n
  • 有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音
  • \n
  • 无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响
  • \n
\n

一个蜂鸣器的简单驱动电路:

\n

\"image-20210821210804064\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <reg52.h>\t//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int

sbit beep = P2^3;//蜂鸣器

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()
{
\twhile(1)
\t{
\t\tbeep = ~beep;//\t蜂鸣器发出滴滴声
\t\tdelay(100);
\t}\t
}
\n

数码管

原理

单个数码管结构原理:

\n

\"image-20210821211542277\"

\n

多个数码管:

\n

\"image-20210821211818619\"

\n

说明:

\n

74HC573是个锁存器,原理如下:

\n

\"image-20210821212211630\"

\n

当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化

\n

当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态

\n

在这幅图

\n

\"image-20210821211818619\"

\n

用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符

\n

在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即1111 1110),最左边这个数码管就被选中了。

\n

数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下

\n
1
2
3
4
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
// 0 1 2 3\t4\t 5\t 6 7 8 9
0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x40, 0x00
//A B C D E F H L - 熄灭
\n

数码管静态显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管位选

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110 选通最左边的数码管
\tWE = 0;//锁存位选数据

\tDU = 1;//打开段选锁存器
\tP0 = 0X5b;//1101 1011 显示“1”
\tDU = 0;//锁存段选数据
\t
\twhile(1);
}
\n

数码管动态显示

要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即*快速扫描每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示

\n

这个代码用来控制3个数码管来显示一个三位数

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <reg52.h>//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

//设计一个函数用于显示三位的数字number
void display(uchar number)
{
\tuchar bai, shi, ge;
\tbai = number / 100; \t\t//百位
\tshi = number % 100 / 10;\t//十位
\tge = number % 10;\t\t\t//个位
\t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[bai];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第二位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFD; //1111 1101
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[shi];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第三位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFB; //1111 1011
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[ge];//
\tDU = 0;//锁存段选数据
\tdelay(5);
}

void main()//main函数自身会循环
{\t
\twhile(1)
\t{
\t\tdisplay(185); //数码管显示185
\t}\t
}
\n

键盘

非编码键盘分为独立键盘和矩阵键盘

\n

独立键盘

\"image-20210821222715524\"

\n

对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测

\n

P30是否为低电平即可

\n
\n

【注意】

\n
    \n
  1. 按键消抖,最简单的是延时10~20ms后再检查其状态是否变化
  2. \n
  3. 松手检测
  4. \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*********************************************************************************
\t按下开发板S2按键数码管值+1,最大到9
\t按下S3按下,值-1,最小减到0
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};


//毫秒级延时函数定义
void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\twhile(1)
\t{
\t\tif(key_s2 == 0)//判断S2是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s2 == 0)
\t\t\t{
\t\t\t\tif(num != 9)//如果值不等于9则+1,功能把值限定为小于9
\t\t\t\tnum++;
\t\t\t\twhile(!key_s2);//松手检测
\t\t\t}\t
\t\t}
\t\tif(key_s3 == 0)//判断S3是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s3 == 0)
\t\t\t{
\t\t\t\tif(num > 0)\t//如果大于0则执行减一
\t\t\t\t\tnum--;
\t\t\t\twhile(!key_s3);//松手检测
\t\t\t}\t
\t\t}
\t\t//松手之后刷新显示
\t\tDU = 1;//打开段选锁存器
\t\tP0 = tabel[num];//
\t\tDU = 0;//锁存段选数据
\t}\t
}
\n

矩阵键盘

判断矩阵按键中某个按键是否按下可采用先列扫描再行扫描的方式

\n
    \n
  1. 列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了;
  2. \n
  3. 行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*********************************************************************************
* 【程序功能】: \t4*4矩阵键盘与4位独立键盘识别\t\t \t\t\t \t\t\t
* 【使用说明】: \t按下矩阵键盘和独立键盘任意键,数码管显示相应数值
\t\t\t\t 初始显示“-”横
**********************************************************************************/
#include <reg52.h>//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
uchar num;//数码管显示的值
uchar KeyValue = 20;//按键值 显示-

//共阴数码管段选表
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x37, 0x3E, 0x40, 0x00 };
//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,H,L,,n,u,-,熄灭


void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void KeyScan()
{
\t//4*4矩阵键盘扫描
\tP3 = 0XF0;//列扫描
\tif(P3 != 0XF0)//判断按键是否被按下
\t{
\t\tdelay(10);//软件消抖10ms
\t\tif(P3 != 0XF0)//判断按键是否被按下
\t\t{
\t\t\tswitch(P3) //判断那一列被按下
\t\t\t{
\t\t\t\tcase 0xe0:\tKeyValue = 0;\tbreak;//第一列被按下
\t\t\t\tcase 0xd0:\tKeyValue = 1;\tbreak;//第二列被按下
\t\t\t\tcase 0xb0:\tKeyValue = 2;\tbreak;//第三列被按下
\t\t\t\tcase 0x70:\tKeyValue = 3;\tbreak;//第四列被按下
\t\t\t}
\t\t\tP3 = 0X0F;//行扫描
\t\t\tswitch(P3) //判断那一行被按下
\t\t\t{
\t\t\t\tcase 0x0e:\tKeyValue = KeyValue;\tbreak;//第一行被按下
\t\t\t\tcase 0x0d:\tKeyValue = KeyValue + 4;\tbreak;//第二行被按下
\t\t\t\tcase 0x0b:\tKeyValue = KeyValue + 8;\tbreak;//第三行被按下
\t\t\t\tcase 0x07:\tKeyValue = KeyValue + 12;\tbreak;//第四行被按下
\t\t\t}
\t\t\twhile(P3 != 0X0F);//松手检测\t
\t\t}
\t}

\tP3 = 0XFF;//独立按键扫描
\tif(P3 != 0XFF)
\t{
\t\tdelay(10);//软件消抖10ms
\t\tif(P3 != 0XFF)
\t\t{
\t\t\tswitch(P3) //判断那一行被按下
\t\t\t{
\t\t\t\tcase 0xfe:\tKeyValue = 16;\tbreak;//S2被按下
\t\t\t\tcase 0xfd:\tKeyValue = 17;\tbreak;//S3被按下
\t\t\t\tcase 0xfb:\tKeyValue = 18;\tbreak;//S4被按下
\t\t\t\tcase 0xf7:\tKeyValue = 19;\tbreak;//S5被按下
\t\t\t}
\t\t\twhile(P3 != 0XFF);//松手检测\t\t\t
\t\t}\t
\t}

}

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据

\tDU = 1;//打开段选锁存器
\twhile(1)
\t{
\t\tKeyScan();//20个按键键盘扫描
\t\tP0 = tabel[KeyValue];//显示按键值
\t}\t
}
\n
\n

总结:先列扫描,再行扫描;或者先行扫描,再列扫描

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

点亮第一个led灯

    \n
  1. 新建工程

    \n

    \"image-20210821203338653\"

    \n
  2. \n
  3. 选择固件

    \n

    \"image-20210821203543242\"

    \n

    \"image-20210821203717166\"

    \n
  4. \n
  5. 新建代码源文件

    \n

    \"image-20210821203932209\"

    \n

    \"image-20210821204130367\"

    \n

    \"image-20210821204227781\"

    \n
  6. \n
  7. 添加如下代码

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    #include <reg52.h> //引用51头文件

    sbit LED = P1^1;

    void main()
    {
    \tLED = 0;\t//点亮LED2\t
    }
  8. \n
  9. 编译前要先配置,让编译器在编译完成后输出hex文件,hex文件用来烧录进单片机里面

    \n

    \"image-20210821204723747\"

    \n
  10. \n
  11. 编译

    \n

    \"image-20210821205109242\"

    \n
  12. \n
  13. 上传(烧录)

    \n

    \"image-20210821205434569\"

    \n
  14. \n
\n

到此第一个点灯小程序结束

\n

流水灯

按照之前的步骤再建立一个新工程,写下如下代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <reg52.h>\t //包含51头文件
#include <intrins.h> //包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

uchar temp; //LED灯相关变量

//延时函数
void delay(uint z)
{
\tuint x, y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()
{
\ttemp = 0xfe;
\tP1 = temp; //1111 1110 初值LED1亮
\tdelay(1000);//毫秒级延时 100毫秒
\twhile(1)
\t{
\t\ttemp = _cror_(temp, 1);//循环左移
\t\tP1 = temp;//移位完成后赋值给P1 每个一个灯点亮
\t\tdelay(1000);//毫秒级延时 100毫秒
\t}\t
}
\n

说明:

\n
    \n
  • intrins.h 头文件包含移位函数 :右移_cror_()左移_corl_()
  • \n
\n

蜂鸣器

蜂鸣器分有源与无源蜂鸣器

\n
    \n
  • 有源指内部自带震荡源,只要提供电压即可响,但由于其震荡源震动频率固定,因此只能产生一种频率的声音
  • \n
  • 无源蜂鸣器指内部没有震荡源,需要提供变化的电压(方波)才能让其响
  • \n
\n

一个蜂鸣器的简单驱动电路:

\n

\"image-20210821210804064\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <reg52.h>\t//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int

sbit beep = P2^3;//蜂鸣器

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()
{
\twhile(1)
\t{
\t\tbeep = ~beep;//\t蜂鸣器发出滴滴声
\t\tdelay(100);
\t}\t
}
\n

数码管

原理

单个数码管结构原理:

\n

\"image-20210821211542277\"

\n

多个数码管:

\n

\"image-20210821211818619\"

\n

说明:

\n

74HC573是个锁存器,原理如下:

\n

\"image-20210821212211630\"

\n

当控制端LE为高电平时,输出端Q的数据随输入端D的数据变化而变化

\n

当控制端LE为低电平时,输入端D的数据变化,Q端会保持之前的状态

\n

在这幅图

\n

\"image-20210821211818619\"

\n

用到2片74HC573模块,上面一片是用来控制哪一个数码管使用,下面那一片用来控制显示的字符

\n

在这8个数码管中,左边是低位,右边是高位,这是共阴极数码管,比如位选输入“0xFE”(即1111 1110),最左边这个数码管就被选中了。

\n

数码管的每一段都接到地,给对应的段提供高电平就能点亮它,其数码表如下

\n
1
2
3
4
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
// 0 1 2 3\t4\t 5\t 6 7 8 9
0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x40, 0x00
//A B C D E F H L - 熄灭
\n

数码管静态显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管位选

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110 选通最左边的数码管
\tWE = 0;//锁存位选数据

\tDU = 1;//打开段选锁存器
\tP0 = 0X5b;//1101 1011 显示“1”
\tDU = 0;//锁存段选数据
\t
\twhile(1);
}
\n

数码管动态显示

要让多个数码管同时显示,如果用静态显示的方式,每个数码管都要8根线连接,n个数码管就要 n8根线,比较耗费资源,因此使用动态扫描的方式进行显示,即*快速扫描每一个数码管,分别让它们进行显示,只要足够快,由于视觉暂留效应,人的大脑会以为这是同时显示,实际上每个时刻只有一个数码管进行了显示

\n

这个代码用来控制3个数码管来显示一个三位数

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <reg52.h>//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

//设计一个函数用于显示三位的数字number
void display(uchar number)
{
\tuchar bai, shi, ge;
\tbai = number / 100; \t\t//百位
\tshi = number % 100 / 10;\t//十位
\tge = number % 10;\t\t\t//个位
\t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[bai];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第二位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFD; //1111 1101
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[shi];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第三位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFB; //1111 1011
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[ge];//
\tDU = 0;//锁存段选数据
\tdelay(5);
}

void main()//main函数自身会循环
{\t
\twhile(1)
\t{
\t\tdisplay(185); //数码管显示185
\t}\t
}
\n

键盘

非编码键盘分为独立键盘和矩阵键盘

\n

独立键盘

\"image-20210821222715524\"

\n

对于独立键盘而言,例如,当S2按键按下时,P30被拉低,变为低电平,因此要判断S2是否被按下,只需要不断检测

\n

P30是否为低电平即可

\n
\n

【注意】

\n
    \n
  1. 按键消抖,最简单的是延时10~20ms后再检查其状态是否变化
  2. \n
  3. 松手检测
  4. \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*********************************************************************************
\t按下开发板S2按键数码管值+1,最大到9
\t按下S3按下,值-1,最小减到0
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};


//毫秒级延时函数定义
void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\twhile(1)
\t{
\t\tif(key_s2 == 0)//判断S2是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s2 == 0)
\t\t\t{
\t\t\t\tif(num != 9)//如果值不等于9则+1,功能把值限定为小于9
\t\t\t\tnum++;
\t\t\t\twhile(!key_s2);//松手检测
\t\t\t}\t
\t\t}
\t\tif(key_s3 == 0)//判断S3是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s3 == 0)
\t\t\t{
\t\t\t\tif(num > 0)\t//如果大于0则执行减一
\t\t\t\t\tnum--;
\t\t\t\twhile(!key_s3);//松手检测
\t\t\t}\t
\t\t}
\t\t//松手之后刷新显示
\t\tDU = 1;//打开段选锁存器
\t\tP0 = tabel[num];//
\t\tDU = 0;//锁存段选数据
\t}\t
}
\n

矩阵键盘

判断矩阵按键中某个按键是否按下可采用先列扫描再行扫描的方式

\n
    \n
  1. 列扫描:先给P37, P36, P35, P34高电平,同时给P33,P32,P31,P30低电平,即传送 “0xF0”(11110000)过去,如果【S6】(或S10/S14/S18)按下,则P34就会被拉低,P3的引脚数据变为“0xE0”(1110 0000),S7按下,P35被拉低,变为“0xD1”(1101 0000),因此只要判断P3这一变量的数据变化即可判断是哪一列被按下了;
  2. \n
  3. 行扫描:给P3发送“0x0F”(0000 1111),如果是【S6】按下,则P30被拉低,P3数据变为“0x0E”(0000 1110),如果是【S10】按下,P3数据变为“0x0D”(0000 1101),这样就可以判断出是哪个按键按下。
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*********************************************************************************
* 【程序功能】: \t4*4矩阵键盘与4位独立键盘识别\t\t \t\t\t \t\t\t
* 【使用说明】: \t按下矩阵键盘和独立键盘任意键,数码管显示相应数值
\t\t\t\t 初始显示“-”横
**********************************************************************************/
#include <reg52.h>//包含51头文件
#include <intrins.h>//包含移位标准库函数头文件

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
uchar num;//数码管显示的值
uchar KeyValue = 20;//按键值 显示-

//共阴数码管段选表
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38, 0x37, 0x3E, 0x40, 0x00 };
//0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,H,L,,n,u,-,熄灭


void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void KeyScan()
{
\t//4*4矩阵键盘扫描
\tP3 = 0XF0;//列扫描
\tif(P3 != 0XF0)//判断按键是否被按下
\t{
\t\tdelay(10);//软件消抖10ms
\t\tif(P3 != 0XF0)//判断按键是否被按下
\t\t{
\t\t\tswitch(P3) //判断那一列被按下
\t\t\t{
\t\t\t\tcase 0xe0:\tKeyValue = 0;\tbreak;//第一列被按下
\t\t\t\tcase 0xd0:\tKeyValue = 1;\tbreak;//第二列被按下
\t\t\t\tcase 0xb0:\tKeyValue = 2;\tbreak;//第三列被按下
\t\t\t\tcase 0x70:\tKeyValue = 3;\tbreak;//第四列被按下
\t\t\t}
\t\t\tP3 = 0X0F;//行扫描
\t\t\tswitch(P3) //判断那一行被按下
\t\t\t{
\t\t\t\tcase 0x0e:\tKeyValue = KeyValue;\tbreak;//第一行被按下
\t\t\t\tcase 0x0d:\tKeyValue = KeyValue + 4;\tbreak;//第二行被按下
\t\t\t\tcase 0x0b:\tKeyValue = KeyValue + 8;\tbreak;//第三行被按下
\t\t\t\tcase 0x07:\tKeyValue = KeyValue + 12;\tbreak;//第四行被按下
\t\t\t}
\t\t\twhile(P3 != 0X0F);//松手检测\t
\t\t}
\t}

\tP3 = 0XFF;//独立按键扫描
\tif(P3 != 0XFF)
\t{
\t\tdelay(10);//软件消抖10ms
\t\tif(P3 != 0XFF)
\t\t{
\t\t\tswitch(P3) //判断那一行被按下
\t\t\t{
\t\t\t\tcase 0xfe:\tKeyValue = 16;\tbreak;//S2被按下
\t\t\t\tcase 0xfd:\tKeyValue = 17;\tbreak;//S3被按下
\t\t\t\tcase 0xfb:\tKeyValue = 18;\tbreak;//S4被按下
\t\t\t\tcase 0xf7:\tKeyValue = 19;\tbreak;//S5被按下
\t\t\t}
\t\t\twhile(P3 != 0XFF);//松手检测\t\t\t
\t\t}\t
\t}

}

void main()//main函数自身会循环
{
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据

\tDU = 1;//打开段选锁存器
\twhile(1)
\t{
\t\tKeyScan();//20个按键键盘扫描
\t\tP0 = tabel[KeyValue];//显示按键值
\t}\t
}
\n
\n

总结:先列扫描,再行扫描;或者先行扫描,再列扫描

\n
\n"},{"title":"51单片机学习笔记(七)","description":"红外通信","abbrlink":"c2d1d169","date":"2021-08-20T08:32:44.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n## 红外通信\n\n### 红外遥控电路组成\n\n一般而言,红外遥控系统由发射装置接收装置两大部分组成\n\n\n\n发射装置(例如遥控器)主要包括:\n\n- 键盘电路\n- 红外编码芯片\n- 电源\n- 红外发射电路\n\n\n\n红外接收设备:\n\n- 可由红外接收电路\n- 红外解码芯片\n- 电源\n- 应用电路组成\n\n\n\n![image-20210824165320963](https://gitee.com/ajream/images/raw/master/img/20210824165322_image-20210824165320963.png)\n\n\n\n\n\n### 信号调制和解调\n\n\n\n为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号\n\n\n\n\n\n### NEC协议\n\n\n\nNEC 标准下的编码表示\n\n1. 引导码高电平约 9000us 左右,低电平约4500us 左右\n\n2. 用户码16 位,数据码16 位,共32位\n\n ![image-20210824165820317](https://gitee.com/ajream/images/raw/master/img/20210824165821_image-20210824165820317.png)\n\n \n\n3. 数据0 是用“高电平约 560us + 低电平约 560us ”表示\n\n4. 数据1 是用“高电平约 560us + 低电平约 1680us”表示\n\n ![image-20210824165845177](https://gitee.com/ajream/images/raw/master/img/20210824165846_image-20210824165845177.png)\n\n\n\n\n\n\n\n## 例子\n\n\n\n```c\n/*\nNEC协议红外通信\n单片机解码后通过串口以9600的比特率发送出去\n*/\n\n#include \n\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit IR = P3^2; //定义红外脉冲数据接口\t外部中断O输入口\n\nuchar IRtime; \t\t//检测红外高电平持续时间(脉宽)\nuchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)\nuchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)\nbit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕\n\nvoid init()\t //初始化定时器0 和外部中断0\n{\n\tTMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装\n\tTH0 = 0x00; //高8位装入0那么定时器溢出一次的时间是256个机器周期\n\tTL0 = 0x00;\n\tEA = 1; //总中断\n\tET0 = 1;\t //定时器0中断\n\tTR0 = 1; //启动定时器0\n\n\tIT0 = 1;\t //设置外部中断0为跳沿触发方式,来一个下降沿触发一次\n\tEX0 = 1;\t //启动外部中断0\n\n\tTH1 = 0xfd; //此溢出率为波特率9600\n\tTL1 = 0xfd;\n\tTR1 = 1; //启动定时器1\n\tSM1 = 1; //设置串口工作方式1,10位异步收发器\n}\n\nvoid time0() interrupt 1 //定义定时器0\n{\n\tIRtime++; \t\t\t //检测脉宽,1次为278us\n}\n\nvoid int0() interrupt 0\t \t\t//定义外部中断0\n{\n\tstatic uchar i;\t \t\t\t//\t声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata\n\tstatic bit startflag;\t\t//开始储存脉宽标志位\n\tif(startflag)\t \t\t\t//开始接收脉宽检测\n\t{\n\t\tif( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us\t\n\t\t这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000 \n\t\t如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/\n\t\t\ti = 0;\t\t\t\t //如果是引导码那么执行i=0把他存到IRdata的第一个位\n\t\tIRdata[i] = IRtime; \t\t //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断\n\t\tIRtime = 0;\t\t\t\t //计数清零,下一个下降沿的时候在存入脉宽\n\t\ti++; \t\t\t\t\t //计数脉宽存入的次数\n\t\tif(i == 33) \t\t\t\t //如果存入34次 数组的下标是从0开始i等于33表示执行了34次\n\t\t{\n\t\t \tIRok = 1;\t\t\t\t //那么表示脉宽检测完毕\n\t\t\ti = 0; \t\t\t\t //把脉宽计数清零准备下次存入\n\t\t}\n\t}\n\telse\t\t \n\t{\n\t\tIRtime = 0; \t\t\t\t //引导码开始进入把脉宽计数清零开始计数\n\t\tstartflag = 1;\t\t\t //开始处理标志位置1\n\t}\n}\n\nvoid IRcordpro() \t\t\t\t //提取它的33次脉宽进行数据解码\n{\n\tuchar i, j, k, cord, value;\t/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位\n\tcord用于取出脉宽的时间判断是否符合1的脉宽时间*/\n\tk = 1; \t\t\t\t\t\t//从第一位脉宽开始取,丢弃引导码脉宽\n\tfor(i = 0; i < 4; i++)\n\t{\n\t\tfor(j = 0; j < 8; j++)\n\t\t{\n\t\t\tcord = IRdata[k];\t //把脉宽存入cord\n\t\t\tif(cord > 5)\t \t\t//如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1\n\t\t\tvalue = value | 0x80;\t/*接收的时候是先接收最低位,\n\t\t\t把最低位先放到value的最高位在和0x08按位或一下\n\t\t\t这样不会改变valua的其他位的数值只会让他最高位为1*/\n\t\t\tif(j < 7)\n\t\t\t{\n\t\t\t\tvalue = value >> 1;\t//value位左移依次接收8位数据。\n\t\t\t}\n\t\t\tk++;\t\t\t\t//每执行一次脉宽位加1\n\t\t}\n\t\tIRcord[i] = value;\t //每处理完一个字节把它放入IRcord数组中。\n\t\tvalue = 0; \t\t\t //清零value方便下次在存入数据\n\t}\n\tIRpro_ok = 1;\t\t\t\t //接收完4个字节后IRpro ok置1表示红外解码完成\t\n}\n\n\nvoid main()\n{\n\tuchar i;\n\tinit();\t//执行初始化定时器0和外部中断0\n\twhile(1)\t//大循环\n\t{\n\t\tif(IRok) //判断脉宽是否检测完毕 \n\t\t{ \n\t\t\tIRcordpro();//根据脉宽解码出4个字节的数据\n\t\t\tIRok = 0;\t//重新等待脉宽检测\n\t\t\tif(IRpro_ok) //判断是否解码完毕 \n\t\t\t{\n\t\t\t\tfor(i = 0; i < 4; i++) \n\t\t\t\t{\t \n\t\t\t\t\tSBUF = IRcord[i];\n\t\t\t\t\twhile(!TI);\n\t\t\t\t\tTI = 0;\n\t\t\t\t}\n\t\t\t\tIRpro_ok = 0;\n\t\t\t}\n\t\t}\t\n\t}\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(七).md","raw":"---\ntitle: 51单片机学习笔记(七)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 红外通信\nabbrlink: c2d1d169\ndate: 2021-08-20 16:32:44\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n## 红外通信\n\n### 红外遥控电路组成\n\n一般而言,红外遥控系统由发射装置接收装置两大部分组成\n\n\n\n发射装置(例如遥控器)主要包括:\n\n- 键盘电路\n- 红外编码芯片\n- 电源\n- 红外发射电路\n\n\n\n红外接收设备:\n\n- 可由红外接收电路\n- 红外解码芯片\n- 电源\n- 应用电路组成\n\n\n\n![image-20210824165320963](https://gitee.com/ajream/images/raw/master/img/20210824165322_image-20210824165320963.png)\n\n\n\n\n\n### 信号调制和解调\n\n\n\n为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号\n\n\n\n\n\n### NEC协议\n\n\n\nNEC 标准下的编码表示\n\n1. 引导码高电平约 9000us 左右,低电平约4500us 左右\n\n2. 用户码16 位,数据码16 位,共32位\n\n ![image-20210824165820317](https://gitee.com/ajream/images/raw/master/img/20210824165821_image-20210824165820317.png)\n\n \n\n3. 数据0 是用“高电平约 560us + 低电平约 560us ”表示\n\n4. 数据1 是用“高电平约 560us + 低电平约 1680us”表示\n\n ![image-20210824165845177](https://gitee.com/ajream/images/raw/master/img/20210824165846_image-20210824165845177.png)\n\n\n\n\n\n\n\n## 例子\n\n\n\n```c\n/*\nNEC协议红外通信\n单片机解码后通过串口以9600的比特率发送出去\n*/\n\n#include \n\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit IR = P3^2; //定义红外脉冲数据接口\t外部中断O输入口\n\nuchar IRtime; \t\t//检测红外高电平持续时间(脉宽)\nuchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)\nuchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)\nbit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕\n\nvoid init()\t //初始化定时器0 和外部中断0\n{\n\tTMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装\n\tTH0 = 0x00; //高8位装入0那么定时器溢出一次的时间是256个机器周期\n\tTL0 = 0x00;\n\tEA = 1; //总中断\n\tET0 = 1;\t //定时器0中断\n\tTR0 = 1; //启动定时器0\n\n\tIT0 = 1;\t //设置外部中断0为跳沿触发方式,来一个下降沿触发一次\n\tEX0 = 1;\t //启动外部中断0\n\n\tTH1 = 0xfd; //此溢出率为波特率9600\n\tTL1 = 0xfd;\n\tTR1 = 1; //启动定时器1\n\tSM1 = 1; //设置串口工作方式1,10位异步收发器\n}\n\nvoid time0() interrupt 1 //定义定时器0\n{\n\tIRtime++; \t\t\t //检测脉宽,1次为278us\n}\n\nvoid int0() interrupt 0\t \t\t//定义外部中断0\n{\n\tstatic uchar i;\t \t\t\t//\t声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata\n\tstatic bit startflag;\t\t//开始储存脉宽标志位\n\tif(startflag)\t \t\t\t//开始接收脉宽检测\n\t{\n\t\tif( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us\t\n\t\t这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000 \n\t\t如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/\n\t\t\ti = 0;\t\t\t\t //如果是引导码那么执行i=0把他存到IRdata的第一个位\n\t\tIRdata[i] = IRtime; \t\t //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断\n\t\tIRtime = 0;\t\t\t\t //计数清零,下一个下降沿的时候在存入脉宽\n\t\ti++; \t\t\t\t\t //计数脉宽存入的次数\n\t\tif(i == 33) \t\t\t\t //如果存入34次 数组的下标是从0开始i等于33表示执行了34次\n\t\t{\n\t\t \tIRok = 1;\t\t\t\t //那么表示脉宽检测完毕\n\t\t\ti = 0; \t\t\t\t //把脉宽计数清零准备下次存入\n\t\t}\n\t}\n\telse\t\t \n\t{\n\t\tIRtime = 0; \t\t\t\t //引导码开始进入把脉宽计数清零开始计数\n\t\tstartflag = 1;\t\t\t //开始处理标志位置1\n\t}\n}\n\nvoid IRcordpro() \t\t\t\t //提取它的33次脉宽进行数据解码\n{\n\tuchar i, j, k, cord, value;\t/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位\n\tcord用于取出脉宽的时间判断是否符合1的脉宽时间*/\n\tk = 1; \t\t\t\t\t\t//从第一位脉宽开始取,丢弃引导码脉宽\n\tfor(i = 0; i < 4; i++)\n\t{\n\t\tfor(j = 0; j < 8; j++)\n\t\t{\n\t\t\tcord = IRdata[k];\t //把脉宽存入cord\n\t\t\tif(cord > 5)\t \t\t//如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1\n\t\t\tvalue = value | 0x80;\t/*接收的时候是先接收最低位,\n\t\t\t把最低位先放到value的最高位在和0x08按位或一下\n\t\t\t这样不会改变valua的其他位的数值只会让他最高位为1*/\n\t\t\tif(j < 7)\n\t\t\t{\n\t\t\t\tvalue = value >> 1;\t//value位左移依次接收8位数据。\n\t\t\t}\n\t\t\tk++;\t\t\t\t//每执行一次脉宽位加1\n\t\t}\n\t\tIRcord[i] = value;\t //每处理完一个字节把它放入IRcord数组中。\n\t\tvalue = 0; \t\t\t //清零value方便下次在存入数据\n\t}\n\tIRpro_ok = 1;\t\t\t\t //接收完4个字节后IRpro ok置1表示红外解码完成\t\n}\n\n\nvoid main()\n{\n\tuchar i;\n\tinit();\t//执行初始化定时器0和外部中断0\n\twhile(1)\t//大循环\n\t{\n\t\tif(IRok) //判断脉宽是否检测完毕 \n\t\t{ \n\t\t\tIRcordpro();//根据脉宽解码出4个字节的数据\n\t\t\tIRok = 0;\t//重新等待脉宽检测\n\t\t\tif(IRpro_ok) //判断是否解码完毕 \n\t\t\t{\n\t\t\t\tfor(i = 0; i < 4; i++) \n\t\t\t\t{\t \n\t\t\t\t\tSBUF = IRcord[i];\n\t\t\t\t\twhile(!TI);\n\t\t\t\t\tTI = 0;\n\t\t\t\t}\n\t\t\t\tIRpro_ok = 0;\n\t\t\t}\n\t\t}\t\n\t}\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(七)","published":1,"updated":"2021-09-10T12:45:26.554Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6nz0003akve5a7o3s93","content":"

红外通信

红外遥控电路组成

一般而言,红外遥控系统由发射装置接收装置两大部分组成

\n

发射装置(例如遥控器)主要包括:

\n
    \n
  • 键盘电路
  • \n
  • 红外编码芯片
  • \n
  • 电源
  • \n
  • 红外发射电路
  • \n
\n

红外接收设备:

\n
    \n
  • 可由红外接收电路
  • \n
  • 红外解码芯片
  • \n
  • 电源
  • \n
  • 应用电路组成
  • \n
\n

\"image-20210824165320963\"

\n

信号调制和解调

为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号

\n

NEC协议

NEC 标准下的编码表示

\n
    \n
  1. 引导码高电平约 9000us 左右,低电平约4500us 左右

    \n
  2. \n
  3. 用户码16 位,数据码16 位,共32位

    \n

    \"image-20210824165820317\"

    \n
  4. \n
\n
    \n
  1. 数据0 是用“高电平约 560us + 低电平约 560us ”表示

    \n
  2. \n
  3. 数据1 是用“高电平约 560us + 低电平约 1680us”表示

    \n

    \"image-20210824165845177\"

    \n
  4. \n
\n

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
NEC协议红外通信
单片机解码后通过串口以9600的比特率发送出去
*/

#include <reg52.h>

/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit IR = P3^2; //定义红外脉冲数据接口\t外部中断O输入口

uchar IRtime; \t\t//检测红外高电平持续时间(脉宽)
uchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)
uchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)
bit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕

void init()\t //初始化定时器0 和外部中断0
{
\tTMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装
\tTH0 = 0x00; //高8位装入0那么定时器溢出一次的时间是256个机器周期
\tTL0 = 0x00;
\tEA = 1; //总中断
\tET0 = 1;\t //定时器0中断
\tTR0 = 1; //启动定时器0

\tIT0 = 1;\t //设置外部中断0为跳沿触发方式,来一个下降沿触发一次
\tEX0 = 1;\t //启动外部中断0

\tTH1 = 0xfd; //此溢出率为波特率9600
\tTL1 = 0xfd;
\tTR1 = 1; //启动定时器1
\tSM1 = 1; //设置串口工作方式1,10位异步收发器
}

void time0() interrupt 1 //定义定时器0
{
\tIRtime++; \t\t\t //检测脉宽,1次为278us
}

void int0() interrupt 0\t \t\t//定义外部中断0
{
\tstatic uchar i;\t \t\t\t//\t声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata
\tstatic bit startflag;\t\t//开始储存脉宽标志位
\tif(startflag)\t \t\t\t//开始接收脉宽检测
\t{
\t\tif( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us\t
\t\t这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000
\t\t如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/
\t\t\ti = 0;\t\t\t\t //如果是引导码那么执行i=0把他存到IRdata的第一个位
\t\tIRdata[i] = IRtime; \t\t //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断
\t\tIRtime = 0;\t\t\t\t //计数清零,下一个下降沿的时候在存入脉宽
\t\ti++; \t\t\t\t\t //计数脉宽存入的次数
\t\tif(i == 33) \t\t\t\t //如果存入34次 数组的下标是从0开始i等于33表示执行了34次
\t\t{
\t\t \tIRok = 1;\t\t\t\t //那么表示脉宽检测完毕
\t\t\ti = 0; \t\t\t\t //把脉宽计数清零准备下次存入
\t\t}
\t}
\telse\t\t
\t{
\t\tIRtime = 0; \t\t\t\t //引导码开始进入把脉宽计数清零开始计数
\t\tstartflag = 1;\t\t\t //开始处理标志位置1
\t}
}

void IRcordpro() \t\t\t\t //提取它的33次脉宽进行数据解码
{
\tuchar i, j, k, cord, value;\t/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位
\tcord用于取出脉宽的时间判断是否符合1的脉宽时间*/
\tk = 1; \t\t\t\t\t\t//从第一位脉宽开始取,丢弃引导码脉宽
\tfor(i = 0; i < 4; i++)
\t{
\t\tfor(j = 0; j < 8; j++)
\t\t{
\t\t\tcord = IRdata[k];\t //把脉宽存入cord
\t\t\tif(cord > 5)\t \t\t//如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1
\t\t\tvalue = value | 0x80;\t/*接收的时候是先接收最低位,
\t\t\t把最低位先放到value的最高位在和0x08按位或一下
\t\t\t这样不会改变valua的其他位的数值只会让他最高位为1*/
\t\t\tif(j < 7)
\t\t\t{
\t\t\t\tvalue = value >> 1;\t//value位左移依次接收8位数据。
\t\t\t}
\t\t\tk++;\t\t\t\t//每执行一次脉宽位加1
\t\t}
\t\tIRcord[i] = value;\t //每处理完一个字节把它放入IRcord数组中。
\t\tvalue = 0; \t\t\t //清零value方便下次在存入数据
\t}
\tIRpro_ok = 1;\t\t\t\t //接收完4个字节后IRpro ok置1表示红外解码完成\t
}


void main()
{
\tuchar i;
\tinit();\t//执行初始化定时器0和外部中断0
\twhile(1)\t//大循环
\t{
\t\tif(IRok) //判断脉宽是否检测完毕
\t\t{
\t\t\tIRcordpro();//根据脉宽解码出4个字节的数据
\t\t\tIRok = 0;\t//重新等待脉宽检测
\t\t\tif(IRpro_ok) //判断是否解码完毕
\t\t\t{
\t\t\t\tfor(i = 0; i < 4; i++)
\t\t\t\t{\t
\t\t\t\t\tSBUF = IRcord[i];
\t\t\t\t\twhile(!TI);
\t\t\t\t\tTI = 0;
\t\t\t\t}
\t\t\t\tIRpro_ok = 0;
\t\t\t}
\t\t}\t
\t}
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

红外通信

红外遥控电路组成

一般而言,红外遥控系统由发射装置接收装置两大部分组成

\n

发射装置(例如遥控器)主要包括:

\n
    \n
  • 键盘电路
  • \n
  • 红外编码芯片
  • \n
  • 电源
  • \n
  • 红外发射电路
  • \n
\n

红外接收设备:

\n
    \n
  • 可由红外接收电路
  • \n
  • 红外解码芯片
  • \n
  • 电源
  • \n
  • 应用电路组成
  • \n
\n

\"image-20210824165320963\"

\n

信号调制和解调

为了使信号更好的传输,一般会把信号进行调制使频率变高,接收到后再通过解调等一系列步骤还原信号

\n

NEC协议

NEC 标准下的编码表示

\n
    \n
  1. 引导码高电平约 9000us 左右,低电平约4500us 左右

    \n
  2. \n
  3. 用户码16 位,数据码16 位,共32位

    \n

    \"image-20210824165820317\"

    \n
  4. \n
\n
    \n
  1. 数据0 是用“高电平约 560us + 低电平约 560us ”表示

    \n
  2. \n
  3. 数据1 是用“高电平约 560us + 低电平约 1680us”表示

    \n

    \"image-20210824165845177\"

    \n
  4. \n
\n

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
NEC协议红外通信
单片机解码后通过串口以9600的比特率发送出去
*/

#include <reg52.h>

/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit IR = P3^2; //定义红外脉冲数据接口\t外部中断O输入口

uchar IRtime; \t\t//检测红外高电平持续时间(脉宽)
uchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)
uchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)
bit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕

void init()\t //初始化定时器0 和外部中断0
{
\tTMOD = 0x22; //定时器0和定时器1工作方式2,8位自动重装
\tTH0 = 0x00; //高8位装入0那么定时器溢出一次的时间是256个机器周期
\tTL0 = 0x00;
\tEA = 1; //总中断
\tET0 = 1;\t //定时器0中断
\tTR0 = 1; //启动定时器0

\tIT0 = 1;\t //设置外部中断0为跳沿触发方式,来一个下降沿触发一次
\tEX0 = 1;\t //启动外部中断0

\tTH1 = 0xfd; //此溢出率为波特率9600
\tTL1 = 0xfd;
\tTR1 = 1; //启动定时器1
\tSM1 = 1; //设置串口工作方式1,10位异步收发器
}

void time0() interrupt 1 //定义定时器0
{
\tIRtime++; \t\t\t //检测脉宽,1次为278us
}

void int0() interrupt 0\t \t\t//定义外部中断0
{
\tstatic uchar i;\t \t\t\t//\t声明静态变量(在跳出函数后在回来执行的时候不会丢失数值)i用于把33次高电平的持续时间存入IRdata
\tstatic bit startflag;\t\t//开始储存脉宽标志位
\tif(startflag)\t \t\t\t//开始接收脉宽检测
\t{
\t\tif( (IRtime < 53) && (IRtime >= 32) ) /*判断是否是引导码,底电平9000us+高4500us\t
\t\t这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000
\t\t如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/
\t\t\ti = 0;\t\t\t\t //如果是引导码那么执行i=0把他存到IRdata的第一个位
\t\tIRdata[i] = IRtime; \t\t //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断
\t\tIRtime = 0;\t\t\t\t //计数清零,下一个下降沿的时候在存入脉宽
\t\ti++; \t\t\t\t\t //计数脉宽存入的次数
\t\tif(i == 33) \t\t\t\t //如果存入34次 数组的下标是从0开始i等于33表示执行了34次
\t\t{
\t\t \tIRok = 1;\t\t\t\t //那么表示脉宽检测完毕
\t\t\ti = 0; \t\t\t\t //把脉宽计数清零准备下次存入
\t\t}
\t}
\telse\t\t
\t{
\t\tIRtime = 0; \t\t\t\t //引导码开始进入把脉宽计数清零开始计数
\t\tstartflag = 1;\t\t\t //开始处理标志位置1
\t}
}

void IRcordpro() \t\t\t\t //提取它的33次脉宽进行数据解码
{
\tuchar i, j, k, cord, value;\t/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位
\tcord用于取出脉宽的时间判断是否符合1的脉宽时间*/
\tk = 1; \t\t\t\t\t\t//从第一位脉宽开始取,丢弃引导码脉宽
\tfor(i = 0; i < 4; i++)
\t{
\t\tfor(j = 0; j < 8; j++)
\t\t{
\t\t\tcord = IRdata[k];\t //把脉宽存入cord
\t\t\tif(cord > 5)\t \t\t//如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1
\t\t\tvalue = value | 0x80;\t/*接收的时候是先接收最低位,
\t\t\t把最低位先放到value的最高位在和0x08按位或一下
\t\t\t这样不会改变valua的其他位的数值只会让他最高位为1*/
\t\t\tif(j < 7)
\t\t\t{
\t\t\t\tvalue = value >> 1;\t//value位左移依次接收8位数据。
\t\t\t}
\t\t\tk++;\t\t\t\t//每执行一次脉宽位加1
\t\t}
\t\tIRcord[i] = value;\t //每处理完一个字节把它放入IRcord数组中。
\t\tvalue = 0; \t\t\t //清零value方便下次在存入数据
\t}
\tIRpro_ok = 1;\t\t\t\t //接收完4个字节后IRpro ok置1表示红外解码完成\t
}


void main()
{
\tuchar i;
\tinit();\t//执行初始化定时器0和外部中断0
\twhile(1)\t//大循环
\t{
\t\tif(IRok) //判断脉宽是否检测完毕
\t\t{
\t\t\tIRcordpro();//根据脉宽解码出4个字节的数据
\t\t\tIRok = 0;\t//重新等待脉宽检测
\t\t\tif(IRpro_ok) //判断是否解码完毕
\t\t\t{
\t\t\t\tfor(i = 0; i < 4; i++)
\t\t\t\t{\t
\t\t\t\t\tSBUF = IRcord[i];
\t\t\t\t\twhile(!TI);
\t\t\t\t\tTI = 0;
\t\t\t\t}
\t\t\t\tIRpro_ok = 0;
\t\t\t}
\t\t}\t
\t}
}
\n"},{"title":"51单片机学习笔记(九)","description":"LCD1602液晶显示器基本操作使用","abbrlink":"17528481","date":"2021-08-21T06:32:19.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n## 简单介绍\n\n### 主要参数\n\n显示容量:16X2个字符\n\n芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)\n\n\n\n\n\n### 引脚说明\n\nRS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个\"格子\");\n\n​\t为1时表示数据输入,即每个”格子“显示的内容\n\nR/W:为1时是读,0是写;\n\nE:读写数据时要置1\n\nD0~D7:数据输入输出\n\n![image-20210825154248774](https://gitee.com/ajream/images/raw/master/img/20210825154252_image-20210825154248774.png)\n\n\n\n\n\n## 基本操作\n\n### 读写操作\n\n- 读状态,输入:RS=0, RW=1, E=0; \n\n 此时输出数据D0~D7为状态字,每一位都有各自的状态;\n\n 其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;\n\n ![image-20210825155602088](https://gitee.com/ajream/images/raw/master/img/20210825155603_image-20210825155602088.png)\n\n- 写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码\n- 读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据\n- 写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲\n\n\n\n```c\nvoid Read_Busy() //读状态判断是芯片否处于”忙“状态\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n Read_Busy();//判断忙\n RS = 0;\n RW = 0;\n P0 = cmd;\n EN = 1;\n EN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\n```\n\n\n\n\n\n### RAM地址映射\n\n控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:\n\n![image-20210825160547241](https://gitee.com/ajream/images/raw/master/img/20210825160548_image-20210825160547241.png)\n\n注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符\n\n\n\n### 指令说明\n\n初始化设置:\n\n1. 显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口\n\n2. 开/关光标设置:(配置时要转为16进制)\n\n 指令码(二进制形式)`00001DCB`\n\n - D=1开显示,D=0关显示\n\n - C=1显示光标,C=0不显示光标\n - B=1光标闪烁,B=0不闪烁\n\n 指令码(二进制形式)`000001NS`\n\n - N=1当读或写一个字符后地址和光标自动+1,N=0则为自动-1;\n\n - S=1当写入一个字符整屏显示左移(N=1)或右移(N=0)达到光标不动屏幕移动的效果;\n\n S=0当写入一个字符,屏幕显示不移动\n\n3. 数据控制\n\n 控制器内部有地址指针,通过发送指令 `80H+地址码` (地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置\n\n4. 其他指令:\n\n ![image-20210825162336247](https://gitee.com/ajream/images/raw/master/img/20210825162337_image-20210825162336247.png)\n\n\n\n\n\n### 时序图\n\n\n\n读操作时序图\n\n\n\n![image-20210825162506774](https://gitee.com/ajream/images/raw/master/img/20210825162508_image-20210825162506774.png)\n\n\n\n写操作时序图\n\n![image-20210825162527365](https://gitee.com/ajream/images/raw/master/img/20210825162528_image-20210825162527365.png)\n\n\n\n时序参数\n\n\n\n![image-20210825162600974](https://gitee.com/ajream/images/raw/master/img/20210825162602_image-20210825162600974.png)\n\n\n\n\n\n## 例子\n\n控制LCD1602显示字符12345\n\n\n\n```c\n#include \n\ntypedef unsigned char uchar;\ntypedef unsigned int uint;\n\nsbit RS = P3^5;\nsbit RW = P3^6;\nsbit EN = P3^4;\n\n//判断液晶忙,如果忙则等待\nvoid Read_Busy()\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n\tRead_Busy();//判断忙\n\tRS = 0;\n\tRW = 0;\n\tP0 = cmd;\n\tEN = 1;\n\tEN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\nvoid main()\n{\n\tWrite_Cmd(0x38);//设置16*2显示\n\tWrite_Cmd(0x0f);//开显示,显示光标,光标闪烁\n\tWrite_Cmd(0x01);//清屏\n\n\tWrite_Cmd(0x06);//地址指针移位命令\n\tWrite_Cmd(0x80 | 0x06);//显示地址\n\n\tWrite_Dat(1 + '0'); //写入的字符要转换位ASCII码\n\tWrite_Dat(2 + '0');\n\tWrite_Dat(3 + '0');\n\tWrite_Dat(4 + '0');\n\tWrite_Dat(5 + '0');\n\twhile(1);\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(九).md","raw":"---\ntitle: 51单片机学习笔记(九)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: LCD1602液晶显示器基本操作使用\nabbrlink: '17528481'\ndate: 2021-08-21 14:32:19\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n## 简单介绍\n\n### 主要参数\n\n显示容量:16X2个字符\n\n芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)\n\n\n\n\n\n### 引脚说明\n\nRS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个\"格子\");\n\n​\t为1时表示数据输入,即每个”格子“显示的内容\n\nR/W:为1时是读,0是写;\n\nE:读写数据时要置1\n\nD0~D7:数据输入输出\n\n![image-20210825154248774](https://gitee.com/ajream/images/raw/master/img/20210825154252_image-20210825154248774.png)\n\n\n\n\n\n## 基本操作\n\n### 读写操作\n\n- 读状态,输入:RS=0, RW=1, E=0; \n\n 此时输出数据D0~D7为状态字,每一位都有各自的状态;\n\n 其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;\n\n ![image-20210825155602088](https://gitee.com/ajream/images/raw/master/img/20210825155603_image-20210825155602088.png)\n\n- 写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码\n- 读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据\n- 写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲\n\n\n\n```c\nvoid Read_Busy() //读状态判断是芯片否处于”忙“状态\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n Read_Busy();//判断忙\n RS = 0;\n RW = 0;\n P0 = cmd;\n EN = 1;\n EN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\n```\n\n\n\n\n\n### RAM地址映射\n\n控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:\n\n![image-20210825160547241](https://gitee.com/ajream/images/raw/master/img/20210825160548_image-20210825160547241.png)\n\n注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符\n\n\n\n### 指令说明\n\n初始化设置:\n\n1. 显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口\n\n2. 开/关光标设置:(配置时要转为16进制)\n\n 指令码(二进制形式)`00001DCB`\n\n - D=1开显示,D=0关显示\n\n - C=1显示光标,C=0不显示光标\n - B=1光标闪烁,B=0不闪烁\n\n 指令码(二进制形式)`000001NS`\n\n - N=1当读或写一个字符后地址和光标自动+1,N=0则为自动-1;\n\n - S=1当写入一个字符整屏显示左移(N=1)或右移(N=0)达到光标不动屏幕移动的效果;\n\n S=0当写入一个字符,屏幕显示不移动\n\n3. 数据控制\n\n 控制器内部有地址指针,通过发送指令 `80H+地址码` (地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置\n\n4. 其他指令:\n\n ![image-20210825162336247](https://gitee.com/ajream/images/raw/master/img/20210825162337_image-20210825162336247.png)\n\n\n\n\n\n### 时序图\n\n\n\n读操作时序图\n\n\n\n![image-20210825162506774](https://gitee.com/ajream/images/raw/master/img/20210825162508_image-20210825162506774.png)\n\n\n\n写操作时序图\n\n![image-20210825162527365](https://gitee.com/ajream/images/raw/master/img/20210825162528_image-20210825162527365.png)\n\n\n\n时序参数\n\n\n\n![image-20210825162600974](https://gitee.com/ajream/images/raw/master/img/20210825162602_image-20210825162600974.png)\n\n\n\n\n\n## 例子\n\n控制LCD1602显示字符12345\n\n\n\n```c\n#include \n\ntypedef unsigned char uchar;\ntypedef unsigned int uint;\n\nsbit RS = P3^5;\nsbit RW = P3^6;\nsbit EN = P3^4;\n\n//判断液晶忙,如果忙则等待\nvoid Read_Busy()\n{\n\tuchar busy;\n\tP0 = 0xff;\n\tRS = 0;\n\tRW = 1;\n\tdo\n\t{\n\t\tEN = 1;\n\t\tbusy = P0;\n\t\tEN = 0;\n\t}while(busy & 0x80);\n}\n//写LCD1602命令一个字节\nvoid Write_Cmd(uchar cmd)\n{\n\tRead_Busy();//判断忙\n\tRS = 0;\n\tRW = 0;\n\tP0 = cmd;\n\tEN = 1;\n\tEN = 0;\n}\n//写一个字节数据\nvoid Write_Dat(uchar dat)\n{\n\tRead_Busy();\n\tRS = 1;\n\tRW = 0;\n\tP0 = dat;\n\tEN = 1;\n\tEN = 0;\n}\nvoid main()\n{\n\tWrite_Cmd(0x38);//设置16*2显示\n\tWrite_Cmd(0x0f);//开显示,显示光标,光标闪烁\n\tWrite_Cmd(0x01);//清屏\n\n\tWrite_Cmd(0x06);//地址指针移位命令\n\tWrite_Cmd(0x80 | 0x06);//显示地址\n\n\tWrite_Dat(1 + '0'); //写入的字符要转换位ASCII码\n\tWrite_Dat(2 + '0');\n\tWrite_Dat(3 + '0');\n\tWrite_Dat(4 + '0');\n\tWrite_Dat(5 + '0');\n\twhile(1);\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(九)","published":1,"updated":"2021-09-10T12:45:12.950Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o30007akve3mey9pm8","content":"

简单介绍

主要参数

显示容量:16X2个字符

\n

芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)

\n

引脚说明

RS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个”格子”);

\n

​ 为1时表示数据输入,即每个”格子“显示的内容

\n

R/W:为1时是读,0是写;

\n

E:读写数据时要置1

\n

D0~D7:数据输入输出

\n

\"image-20210825154248774\"

\n

基本操作

读写操作

    \n
  • 读状态,输入:RS=0, RW=1, E=0;

    \n

    此时输出数据D0~D7为状态字,每一位都有各自的状态;

    \n

    其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;

    \n

    \"image-20210825155602088\"

    \n
  • \n
  • 写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码

    \n
  • \n
  • 读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据
  • \n
  • 写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void Read_Busy() //读状态判断是芯片否处于”忙“状态
{
\tuchar busy;
\tP0 = 0xff;
\tRS = 0;
\tRW = 1;
\tdo
\t{
\t\tEN = 1;
\t\tbusy = P0;
\t\tEN = 0;
\t}while(busy & 0x80);
}
//写LCD1602命令一个字节
void Write_Cmd(uchar cmd)
{
Read_Busy();//判断忙
RS = 0;
RW = 0;
P0 = cmd;
EN = 1;
EN = 0;
}
//写一个字节数据
void Write_Dat(uchar dat)
{
\tRead_Busy();
\tRS = 1;
\tRW = 0;
\tP0 = dat;
\tEN = 1;
\tEN = 0;
}
\n

RAM地址映射

控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:

\n

\"image-20210825160547241\"

\n

注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符

\n

指令说明

初始化设置:

\n
    \n
  1. 显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口

    \n
  2. \n
  3. 开/关光标设置:(配置时要转为16进制)

    \n

    指令码(二进制形式)00001DCB

    \n
      \n
    • D=1开显示,D=0关显示

      \n
    • \n
    • C=1显示光标,C=0不显示光标

      \n
    • \n
    • B=1光标闪烁,B=0不闪烁
    • \n
    \n

    指令码(二进制形式)000001NS

    \n
      \n
    • N=1当读或写一个字符后地址和光标自动+1,N=0则为自动-1;

      \n
    • \n
    • S=1当写入一个字符整屏显示左移(N=1)或右移(N=0)达到光标不动屏幕移动的效果;

      \n

      S=0当写入一个字符,屏幕显示不移动

      \n
    • \n
    \n
  4. \n
  5. 数据控制

    \n

    控制器内部有地址指针,通过发送指令 80H+地址码 (地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置

    \n
  6. \n
  7. 其他指令:

    \n

    \"image-20210825162336247\"

    \n
  8. \n
\n

时序图

读操作时序图

\n

\"image-20210825162506774\"

\n

写操作时序图

\n

\"image-20210825162527365\"

\n

时序参数

\n

\"image-20210825162600974\"

\n

例子

控制LCD1602显示字符12345

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <reg52.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit RS = P3^5;
sbit RW = P3^6;
sbit EN = P3^4;

//判断液晶忙,如果忙则等待
void Read_Busy()
{
\tuchar busy;
\tP0 = 0xff;
\tRS = 0;
\tRW = 1;
\tdo
\t{
\t\tEN = 1;
\t\tbusy = P0;
\t\tEN = 0;
\t}while(busy & 0x80);
}
//写LCD1602命令一个字节
void Write_Cmd(uchar cmd)
{
\tRead_Busy();//判断忙
\tRS = 0;
\tRW = 0;
\tP0 = cmd;
\tEN = 1;
\tEN = 0;
}
//写一个字节数据
void Write_Dat(uchar dat)
{
\tRead_Busy();
\tRS = 1;
\tRW = 0;
\tP0 = dat;
\tEN = 1;
\tEN = 0;
}
void main()
{
\tWrite_Cmd(0x38);//设置16*2显示
\tWrite_Cmd(0x0f);//开显示,显示光标,光标闪烁
\tWrite_Cmd(0x01);//清屏

\tWrite_Cmd(0x06);//地址指针移位命令
\tWrite_Cmd(0x80 | 0x06);//显示地址

\tWrite_Dat(1 + '0'); //写入的字符要转换位ASCII码
\tWrite_Dat(2 + '0');
\tWrite_Dat(3 + '0');
\tWrite_Dat(4 + '0');
\tWrite_Dat(5 + '0');
\twhile(1);
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

简单介绍

主要参数

显示容量:16X2个字符

\n

芯片工作电压:4.5~5.5V, 最佳工作电压5.0V(2.0mA)

\n

引脚说明

RS:为0时是指令输入,也就是表示显示在哪个“格子”的(1602有16X2共32个”格子”);

\n

​ 为1时表示数据输入,即每个”格子“显示的内容

\n

R/W:为1时是读,0是写;

\n

E:读写数据时要置1

\n

D0~D7:数据输入输出

\n

\"image-20210825154248774\"

\n

基本操作

读写操作

    \n
  • 读状态,输入:RS=0, RW=1, E=0;

    \n

    此时输出数据D0~D7为状态字,每一位都有各自的状态;

    \n

    其中D0-D6表示当前数据地址指针的数值,D7的状态表示读写操作使能,如果是1表示禁止读写,0表示允许读写;

    \n

    \"image-20210825155602088\"

    \n
  • \n
  • 写指令(即告诉1602要显示的位置):RS=0, RW=0, E=高脉冲,D0-D7为指令码

    \n
  • \n
  • 读数据:RS=1, RW=1, E=1, 此时D0-D7输出数据
  • \n
  • 写数据:RS=1, RW=0, D0-D7表示要写入的数据,E=高脉冲
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void Read_Busy() //读状态判断是芯片否处于”忙“状态
{
\tuchar busy;
\tP0 = 0xff;
\tRS = 0;
\tRW = 1;
\tdo
\t{
\t\tEN = 1;
\t\tbusy = P0;
\t\tEN = 0;
\t}while(busy & 0x80);
}
//写LCD1602命令一个字节
void Write_Cmd(uchar cmd)
{
Read_Busy();//判断忙
RS = 0;
RW = 0;
P0 = cmd;
EN = 1;
EN = 0;
}
//写一个字节数据
void Write_Dat(uchar dat)
{
\tRead_Busy();
\tRS = 1;
\tRW = 0;
\tP0 = dat;
\tEN = 1;
\tEN = 0;
}
\n

RAM地址映射

控制器内部自带了80X8位(即80Bytes)的RAM缓冲区,对应关系如下图:

\n

\"image-20210825160547241\"

\n

注意:第10——27和50——67的地址上的字符是无法显示的,只能显示前面16X2个地址上所存放的字符

\n

指令说明

初始化设置:

\n
    \n
  1. 显示模式设置:指令码0x38(二进制是00111000),表示设置16X2显示,5X7点阵,8位数据接口

    \n
  2. \n
  3. 开/关光标设置:(配置时要转为16进制)

    \n

    指令码(二进制形式)00001DCB

    \n
      \n
    • D=1开显示,D=0关显示

      \n
    • \n
    • C=1显示光标,C=0不显示光标

      \n
    • \n
    • B=1光标闪烁,B=0不闪烁
    • \n
    \n

    指令码(二进制形式)000001NS

    \n
      \n
    • N=1当读或写一个字符后地址和光标自动+1,N=0则为自动-1;

      \n
    • \n
    • S=1当写入一个字符整屏显示左移(N=1)或右移(N=0)达到光标不动屏幕移动的效果;

      \n

      S=0当写入一个字符,屏幕显示不移动

      \n
    • \n
    \n
  4. \n
  5. 数据控制

    \n

    控制器内部有地址指针,通过发送指令 80H+地址码 (地址码范围是0——27H,40H——67H) 即可访问地址上的数据,进而进行设置

    \n
  6. \n
  7. 其他指令:

    \n

    \"image-20210825162336247\"

    \n
  8. \n
\n

时序图

读操作时序图

\n

\"image-20210825162506774\"

\n

写操作时序图

\n

\"image-20210825162527365\"

\n

时序参数

\n

\"image-20210825162600974\"

\n

例子

控制LCD1602显示字符12345

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <reg52.h>

typedef unsigned char uchar;
typedef unsigned int uint;

sbit RS = P3^5;
sbit RW = P3^6;
sbit EN = P3^4;

//判断液晶忙,如果忙则等待
void Read_Busy()
{
\tuchar busy;
\tP0 = 0xff;
\tRS = 0;
\tRW = 1;
\tdo
\t{
\t\tEN = 1;
\t\tbusy = P0;
\t\tEN = 0;
\t}while(busy & 0x80);
}
//写LCD1602命令一个字节
void Write_Cmd(uchar cmd)
{
\tRead_Busy();//判断忙
\tRS = 0;
\tRW = 0;
\tP0 = cmd;
\tEN = 1;
\tEN = 0;
}
//写一个字节数据
void Write_Dat(uchar dat)
{
\tRead_Busy();
\tRS = 1;
\tRW = 0;
\tP0 = dat;
\tEN = 1;
\tEN = 0;
}
void main()
{
\tWrite_Cmd(0x38);//设置16*2显示
\tWrite_Cmd(0x0f);//开显示,显示光标,光标闪烁
\tWrite_Cmd(0x01);//清屏

\tWrite_Cmd(0x06);//地址指针移位命令
\tWrite_Cmd(0x80 | 0x06);//显示地址

\tWrite_Dat(1 + '0'); //写入的字符要转换位ASCII码
\tWrite_Dat(2 + '0');
\tWrite_Dat(3 + '0');
\tWrite_Dat(4 + '0');
\tWrite_Dat(5 + '0');
\twhile(1);
}
\n"},{"title":"51单片机学习笔记(三)","description":"串口通信","abbrlink":"383e39e3","date":"2021-08-19T05:17:20.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n# 串口通信\n\n## 原理简介\n\n通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式\n\n**并行通信**\n\n并行通信通常是将数据字节的各位用多条数据线同时进行传送\n\n特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。\n\n![image-20210822131931271](https://gitee.com/ajream/images/raw/master/img/20210822131937_image-20210822131931271.png)\n\n**串行通信**\n\n串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。\n\n![image-20210822132211661](https://gitee.com/ajream/images/raw/master/img/20210822132216_image-20210822132211661.png)\n\n**同步、异步通信**\n\n串行通信又分 同步、异步两种\n\n同步:接收方与发送方时钟一致\n\n异步:双方使用各自的时钟,但应该尽可能保持一致\n\n**数据传输方向**\n\n单工:一台设备只能收/发\n\n半双工:同一时间只能收或发\n\n全双工:可同时进行收发\n\n![image-20210822134038629](https://gitee.com/ajream/images/raw/master/img/20210822134041_image-20210822134038629.png)\n\n**奇偶校验**\n\n在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。\n\n- 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;\n\n- 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。\n\n**代码和校验**\n\n代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。\n\n**循环冗余校验**\n\n这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。\n\n\n\n## 使用串口进行数据发送和接收\n\n### 初始化配置\n\n1. **打开总中断和串口中断**\n\n ![image-20210822141906412](https://gitee.com/ajream/images/raw/master/img/20210822141909_image-20210822141906412.png)\n2. **选择串口工作方式(配置串口控制寄存器SCON)**不懂就先选方式1\n\n ![image-20210822142218968](https://gitee.com/ajream/images/raw/master/img/20210822142220_image-20210822142218968.png)\n3. **允许串口接收(配置串口控制寄存器SCON)**\n\n ![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n ![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n4. **配置波特率**\n\n 假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD\n\n ![image-20210822143113859](https://gitee.com/ajream/images/raw/master/img/20210822143115_image-20210822143113859.png)\n\n 代码如下:\n\n ```c\n void UARTInit()\n {\n \tEA = 1;\t//打开总中断\n \tES = 1; //打开串口中断\n \tSM0 = 0;\tSM1 = 1; //串口工作方式1,8位UART波特率可变\n \tREN = 1;//串口允许接收\n \n \tTR1 = 1;//启动定时器1\n \tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n \tTH1 = 0xfd;\n \tTL1 = 0xfd;//设置比特率9600\n }\n ```\n\n > 说明:TMOD |= 1, 使用了 `|=` 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式\n >\n > ![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n >\n > \n >\n > 这是T0的初始化:\n >\n > ```c\n > void T0_init()\n > {\n > \tEA = 1;\t//打开总中断\n > \tET0 = 1;//打开定时器0中断\n > \tTR0 = 1;\t //启动定时器0\n > \tREN = 1;//允许串口接收\n > \tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n > \tTH0 = 0xED;\n > \tTL0 = 0xFF; //定时5ms\n > }\n > \n > void UARTInit(){\n > //......\n > }\n > ```\n\n \n\n \n\n### 发送、接收数据\n\n\n\n![image-20210822145358670](https://gitee.com/ajream/images/raw/master/img/20210822145359_image-20210822145358670.png)\n\n接收:通过读取接收缓冲器SBUF中的数据\n\n发送:通过往发送缓冲器SBUF写入数据\n\n发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99\n\nRI、TI看这2幅图,如下\n\n![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n\n```c\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)\t\t\t\t\t//判断接收是否完成\n\t{\n\t\tnum = SBUF;\t\t\t//读SBUF,读出串口接收到的数据\n\t\tRI = 0;\t\t\t\t//软件清零接收标志位\t\n\t\ttemp = num;\n\t\tSBUF = ++temp; \t//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)\t\t\t\t\t//判断是否发送完成\n\t\tTI = 0;\t\t\t\t//清零发送完成标志位\t\n} \n```\n\n\n\n\n\n完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tREN = 1;//允许串口接收\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\n//串口初始化\nvoid UART_init()\n{\n\tEA = 1;\t//打开总中断\n\tES = 1; //打开串口中断\n\tSM0 = 0;\tSM1 = 1;//串口工作方式1,8位UART波特率可变\n\tREN = 1;//串口允许接收\n\tTR1 = 1;//启动定时器1\n\tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n\tTH1 = 0xfd;\n\tTL1 = 0xfd;//设置比特率9600\n}\nvoid main()//main函数自身会循环\n{\t\n\tT0_init();//定时器0初始化\n\tUART_init();//串口初始化\n\twhile(1);\t\n} \n\n//定时器0中断函数\nvoid t0() interrupt 1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n}\n\n//串口中断函数\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)//判断接收是否完成\n\t{\n\t\tnum = SBUF;//读SBUF,读出串口接收到的数据\n\t\tRI = 0;//软件清零接收标志位\t\n\t\ttemp = num;//\n\t\tSBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)//判断是否发送完成\n\t\tTI = 0;//清零发送完成标志位\t\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(三).md","raw":"---\ntitle: 51单片机学习笔记(三)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 串口通信\nabbrlink: 383e39e3\ndate: 2021-08-19 13:17:20\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n# 串口通信\n\n## 原理简介\n\n通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式\n\n**并行通信**\n\n并行通信通常是将数据字节的各位用多条数据线同时进行传送\n\n特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。\n\n![image-20210822131931271](https://gitee.com/ajream/images/raw/master/img/20210822131937_image-20210822131931271.png)\n\n**串行通信**\n\n串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。\n\n![image-20210822132211661](https://gitee.com/ajream/images/raw/master/img/20210822132216_image-20210822132211661.png)\n\n**同步、异步通信**\n\n串行通信又分 同步、异步两种\n\n同步:接收方与发送方时钟一致\n\n异步:双方使用各自的时钟,但应该尽可能保持一致\n\n**数据传输方向**\n\n单工:一台设备只能收/发\n\n半双工:同一时间只能收或发\n\n全双工:可同时进行收发\n\n![image-20210822134038629](https://gitee.com/ajream/images/raw/master/img/20210822134041_image-20210822134038629.png)\n\n**奇偶校验**\n\n在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。\n\n- 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;\n\n- 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。\n\n**代码和校验**\n\n代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。\n\n**循环冗余校验**\n\n这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。\n\n\n\n## 使用串口进行数据发送和接收\n\n### 初始化配置\n\n1. **打开总中断和串口中断**\n\n ![image-20210822141906412](https://gitee.com/ajream/images/raw/master/img/20210822141909_image-20210822141906412.png)\n2. **选择串口工作方式(配置串口控制寄存器SCON)**不懂就先选方式1\n\n ![image-20210822142218968](https://gitee.com/ajream/images/raw/master/img/20210822142220_image-20210822142218968.png)\n3. **允许串口接收(配置串口控制寄存器SCON)**\n\n ![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n ![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n4. **配置波特率**\n\n 假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD\n\n ![image-20210822143113859](https://gitee.com/ajream/images/raw/master/img/20210822143115_image-20210822143113859.png)\n\n 代码如下:\n\n ```c\n void UARTInit()\n {\n \tEA = 1;\t//打开总中断\n \tES = 1; //打开串口中断\n \tSM0 = 0;\tSM1 = 1; //串口工作方式1,8位UART波特率可变\n \tREN = 1;//串口允许接收\n \n \tTR1 = 1;//启动定时器1\n \tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n \tTH1 = 0xfd;\n \tTL1 = 0xfd;//设置比特率9600\n }\n ```\n\n > 说明:TMOD |= 1, 使用了 `|=` 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式\n >\n > ![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n >\n > \n >\n > 这是T0的初始化:\n >\n > ```c\n > void T0_init()\n > {\n > \tEA = 1;\t//打开总中断\n > \tET0 = 1;//打开定时器0中断\n > \tTR0 = 1;\t //启动定时器0\n > \tREN = 1;//允许串口接收\n > \tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n > \tTH0 = 0xED;\n > \tTL0 = 0xFF; //定时5ms\n > }\n > \n > void UARTInit(){\n > //......\n > }\n > ```\n\n \n\n \n\n### 发送、接收数据\n\n\n\n![image-20210822145358670](https://gitee.com/ajream/images/raw/master/img/20210822145359_image-20210822145358670.png)\n\n接收:通过读取接收缓冲器SBUF中的数据\n\n发送:通过往发送缓冲器SBUF写入数据\n\n发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99\n\nRI、TI看这2幅图,如下\n\n![image-20210822142735027](https://gitee.com/ajream/images/raw/master/img/20210822142736_image-20210822142735027.png)\n\n![image-20210822150203890](https://gitee.com/ajream/images/raw/master/img/20210822150205_image-20210822150203890.png)\n\n```c\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)\t\t\t\t\t//判断接收是否完成\n\t{\n\t\tnum = SBUF;\t\t\t//读SBUF,读出串口接收到的数据\n\t\tRI = 0;\t\t\t\t//软件清零接收标志位\t\n\t\ttemp = num;\n\t\tSBUF = ++temp; \t//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)\t\t\t\t\t//判断是否发送完成\n\t\tTI = 0;\t\t\t\t//清零发送完成标志位\t\n} \n```\n\n\n\n\n\n完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出\n\n```c\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nuchar num;//数码管显示的值\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tREN = 1;//允许串口接收\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\n//串口初始化\nvoid UART_init()\n{\n\tEA = 1;\t//打开总中断\n\tES = 1; //打开串口中断\n\tSM0 = 0;\tSM1 = 1;//串口工作方式1,8位UART波特率可变\n\tREN = 1;//串口允许接收\n\tTR1 = 1;//启动定时器1\n\tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装\n\tTH1 = 0xfd;\n\tTL1 = 0xfd;//设置比特率9600\n}\nvoid main()//main函数自身会循环\n{\t\n\tT0_init();//定时器0初始化\n\tUART_init();//串口初始化\n\twhile(1);\t\n} \n\n//定时器0中断函数\nvoid t0() interrupt 1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n}\n\n//串口中断函数\nvoid UART() interrupt 4\n{\n\tuchar temp;\n\tif(RI)//判断接收是否完成\n\t{\n\t\tnum = SBUF;//读SBUF,读出串口接收到的数据\n\t\tRI = 0;//软件清零接收标志位\t\n\t\ttemp = num;//\n\t\tSBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器\n\t}\n\tif(TI)//判断是否发送完成\n\t\tTI = 0;//清零发送完成标志位\t\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(三)","published":1,"updated":"2021-09-10T12:45:31.265Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o40009akvehm657ia2","content":"

串口通信

原理简介

通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式

\n

并行通信

\n

并行通信通常是将数据字节的各位用多条数据线同时进行传送

\n

特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。

\n

\"image-20210822131931271\"

\n

串行通信

\n

串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。

\n

\"image-20210822132211661\"

\n

同步、异步通信

\n

串行通信又分 同步、异步两种

\n

同步:接收方与发送方时钟一致

\n

异步:双方使用各自的时钟,但应该尽可能保持一致

\n

数据传输方向

\n

单工:一台设备只能收/发

\n

半双工:同一时间只能收或发

\n

全双工:可同时进行收发

\n

\"image-20210822134038629\"

\n

奇偶校验

\n

在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。

\n
    \n
  • 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;

    \n
  • \n
  • 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。

    \n
  • \n
\n

代码和校验

\n

代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。

\n

循环冗余校验

\n

这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。

\n

使用串口进行数据发送和接收

初始化配置

    \n
  1. 打开总中断和串口中断

    \n

    \"image-20210822141906412\"

    \n
  2. \n
  3. 选择串口工作方式(配置串口控制寄存器SCON)不懂就先选方式1

    \n

    \"image-20210822142218968\"

    \n
  4. \n
  5. 允许串口接收(配置串口控制寄存器SCON)

    \n

    \"image-20210822142735027\"

    \n

    \"image-20210822150203890\"

    \n
  6. \n
  7. 配置波特率

    \n

    假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD

    \n

    \"image-20210822143113859\"

    \n

    代码如下:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void UARTInit()
    {
    \tEA = 1;\t//打开总中断
    \tES = 1; //打开串口中断
    \tSM0 = 0;\tSM1 = 1; //串口工作方式1,8位UART波特率可变
    \tREN = 1;//串口允许接收

    \tTR1 = 1;//启动定时器1
    \tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装
    \tTH1 = 0xfd;
    \tTL1 = 0xfd;//设置比特率9600
    }
    \n
    \n

    说明:TMOD |= 1, 使用了 |= 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式

    \n

    \"image-20210822100638172\"

    \n

    这是T0的初始化:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void T0_init()
    {
    \tEA = 1;\t//打开总中断
    \tET0 = 1;//打开定时器0中断
    \tTR0 = 1;\t //启动定时器0
    \tREN = 1;//允许串口接收
    \tTMOD |= 0X01; //定时器工作模式1,16位定时模式
    \tTH0 = 0xED;
    \tTL0 = 0xFF; //定时5ms
    }

    void UARTInit(){
    //......
    }
    \n
    \n
  8. \n
\n

发送、接收数据

\"image-20210822145358670\"

\n

接收:通过读取接收缓冲器SBUF中的数据

\n

发送:通过往发送缓冲器SBUF写入数据

\n

发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99

\n

RI、TI看这2幅图,如下

\n

\"image-20210822142735027\"

\n

\"image-20210822150203890\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
void UART() interrupt 4
{
\tuchar temp;
\tif(RI)\t\t\t\t\t//判断接收是否完成
\t{
\t\tnum = SBUF;\t\t\t//读SBUF,读出串口接收到的数据
\t\tRI = 0;\t\t\t\t//软件清零接收标志位\t
\t\ttemp = num;
\t\tSBUF = ++temp; \t//写SBUF,把要发送的数据送给发送缓存器
\t}
\tif(TI)\t\t\t\t\t//判断是否发送完成
\t\tTI = 0;\t\t\t\t//清零发送完成标志位\t
}
\n

完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
uchar num;//数码管显示的值

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};


void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void T0_init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tREN = 1;//允许串口接收
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}

//串口初始化
void UART_init()
{
\tEA = 1;\t//打开总中断
\tES = 1; //打开串口中断
\tSM0 = 0;\tSM1 = 1;//串口工作方式1,8位UART波特率可变
\tREN = 1;//串口允许接收
\tTR1 = 1;//启动定时器1
\tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装
\tTH1 = 0xfd;
\tTL1 = 0xfd;//设置比特率9600
}
void main()//main函数自身会循环
{\t
\tT0_init();//定时器0初始化
\tUART_init();//串口初始化
\twhile(1);\t
}

//定时器0中断函数
void t0() interrupt 1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}

//串口中断函数
void UART() interrupt 4
{
\tuchar temp;
\tif(RI)//判断接收是否完成
\t{
\t\tnum = SBUF;//读SBUF,读出串口接收到的数据
\t\tRI = 0;//软件清零接收标志位\t
\t\ttemp = num;//
\t\tSBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器
\t}
\tif(TI)//判断是否发送完成
\t\tTI = 0;//清零发送完成标志位\t
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

串口通信

原理简介

通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式

\n

并行通信

\n

并行通信通常是将数据字节的各位用多条数据线同时进行传送

\n

特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。

\n

\"image-20210822131931271\"

\n

串行通信

\n

串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。

\n

\"image-20210822132211661\"

\n

同步、异步通信

\n

串行通信又分 同步、异步两种

\n

同步:接收方与发送方时钟一致

\n

异步:双方使用各自的时钟,但应该尽可能保持一致

\n

数据传输方向

\n

单工:一台设备只能收/发

\n

半双工:同一时间只能收或发

\n

全双工:可同时进行收发

\n

\"image-20210822134038629\"

\n

奇偶校验

\n

在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。

\n
    \n
  • 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;

    \n
  • \n
  • 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。

    \n
  • \n
\n

代码和校验

\n

代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。

\n

循环冗余校验

\n

这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。

\n

使用串口进行数据发送和接收

初始化配置

    \n
  1. 打开总中断和串口中断

    \n

    \"image-20210822141906412\"

    \n
  2. \n
  3. 选择串口工作方式(配置串口控制寄存器SCON)不懂就先选方式1

    \n

    \"image-20210822142218968\"

    \n
  4. \n
  5. 允许串口接收(配置串口控制寄存器SCON)

    \n

    \"image-20210822142735027\"

    \n

    \"image-20210822150203890\"

    \n
  6. \n
  7. 配置波特率

    \n

    假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD

    \n

    \"image-20210822143113859\"

    \n

    代码如下:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void UARTInit()
    {
    \tEA = 1;\t//打开总中断
    \tES = 1; //打开串口中断
    \tSM0 = 0;\tSM1 = 1; //串口工作方式1,8位UART波特率可变
    \tREN = 1;//串口允许接收

    \tTR1 = 1;//启动定时器1
    \tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装
    \tTH1 = 0xfd;
    \tTL1 = 0xfd;//设置比特率9600
    }
    \n
    \n

    说明:TMOD |= 1, 使用了 |= 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式

    \n

    \"image-20210822100638172\"

    \n

    这是T0的初始化:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void T0_init()
    {
    \tEA = 1;\t//打开总中断
    \tET0 = 1;//打开定时器0中断
    \tTR0 = 1;\t //启动定时器0
    \tREN = 1;//允许串口接收
    \tTMOD |= 0X01; //定时器工作模式1,16位定时模式
    \tTH0 = 0xED;
    \tTL0 = 0xFF; //定时5ms
    }

    void UARTInit(){
    //......
    }
    \n
    \n
  8. \n
\n

发送、接收数据

\"image-20210822145358670\"

\n

接收:通过读取接收缓冲器SBUF中的数据

\n

发送:通过往发送缓冲器SBUF写入数据

\n

发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99

\n

RI、TI看这2幅图,如下

\n

\"image-20210822142735027\"

\n

\"image-20210822150203890\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
void UART() interrupt 4
{
\tuchar temp;
\tif(RI)\t\t\t\t\t//判断接收是否完成
\t{
\t\tnum = SBUF;\t\t\t//读SBUF,读出串口接收到的数据
\t\tRI = 0;\t\t\t\t//软件清零接收标志位\t
\t\ttemp = num;
\t\tSBUF = ++temp; \t//写SBUF,把要发送的数据送给发送缓存器
\t}
\tif(TI)\t\t\t\t\t//判断是否发送完成
\t\tTI = 0;\t\t\t\t//清零发送完成标志位\t
}
\n

完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
uchar num;//数码管显示的值

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};


void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void T0_init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tREN = 1;//允许串口接收
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}

//串口初始化
void UART_init()
{
\tEA = 1;\t//打开总中断
\tES = 1; //打开串口中断
\tSM0 = 0;\tSM1 = 1;//串口工作方式1,8位UART波特率可变
\tREN = 1;//串口允许接收
\tTR1 = 1;//启动定时器1
\tTMOD |= 0x20;//定时器1,工作模式2 8位自动重装
\tTH1 = 0xfd;
\tTL1 = 0xfd;//设置比特率9600
}
void main()//main函数自身会循环
{\t
\tT0_init();//定时器0初始化
\tUART_init();//串口初始化
\twhile(1);\t
}

//定时器0中断函数
void t0() interrupt 1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}

//串口中断函数
void UART() interrupt 4
{
\tuchar temp;
\tif(RI)//判断接收是否完成
\t{
\t\tnum = SBUF;//读SBUF,读出串口接收到的数据
\t\tRI = 0;//软件清零接收标志位\t
\t\ttemp = num;//
\t\tSBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器
\t}
\tif(TI)//判断是否发送完成
\t\tTI = 0;//清零发送完成标志位\t
}
\n"},{"title":"51单片机学习笔记(二)","description":"定时-计数器、中断系统","abbrlink":"46cd19c8","date":"2021-08-19T00:57:20.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n# 定时计数器\n\n51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)\n\n这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。\n\n当工作在**定时模式**时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。\n可以算出工作在定时模式时最高单次定时时间:65535*1.085us\n\n当工作在**计数器模式**时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1\n\n\n## 使用定时-计数器\n### (一)启动定时计数器\n\nT0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)\n\n![image-20210822095401344](https://gitee.com/ajream/images/raw/master/img/20210822095407_image-20210822095401344.png)\n\n> 总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)\n\n### (二)设定定时计数器的工作模式\n\n这一步由寄存器TMOD(不可位寻址)控制\n\n![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n\n第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数\n\n第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式\n\nM1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:\n\n| M1 | M0 | 工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位) |\n| ---- | ---- | ------------------------------------------------------------ |\n| 0 | 0 | 13位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$ |\n| 0 | 1 | 16位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535 |\n| 1 | 0 | 8位自动重装载方式,溢出时自动将THx的值装载到TLx里面 |\n| 1 | 1 | 如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出) |\n\n### (三)查询定时计数器是否溢出\n\n上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0\n\n\n\n```c\n/******************************************************************\n* 【程序功能】: \t定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。\t\n*******************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tuchar bai, shi, ge;\n\tbai = i / 100; //236 / 100 = 2\n\tshi = i % 100 / 10;\t//236 % 100 / 10 = 3\n\tge = i % 10;//236 % 10 =6\n\t\n\t//第一位数码管 \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\n//定时器T0初始化\nvoid T0_init()\n{\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时器计数模式\n\tTH0 = 0x4b;\n\tTL0 = 0xfd; //定时50ms\n //0x4bfd即19453,共计数65535-19453+1 = 46083次,耗时46083*1.085us = 50 ms\n}\n\nvoid main()//main函数自身会循环\n{\t\n\tuchar mSec, Sec;//毫秒和秒储存变量\n\tT0_init();//定时器T0初始化\n\twhile(1)\n\t{\n\t\tif(TF0 == 1)//判断是否溢出\n\t\t{\n\t\t\tTF0 = 0;//软件清零溢出标志位\n\t\t\tTH0 = 0x4b;\n\t\t\tTL0 = 0xfd; //定时50ms\n\t\t\tmSec++;//50ms到\n\t\t\tif(mSec == 20)\n\t\t\t{\n\t\t\t\tmSec = 0;\n\t\t\t\tSec++;//1秒时间到\n\t\t\t}\t\t\t\t\t\n\t\t}\n\t\tdisplay(Sec); //数码管显示函数\n\t\tif(Sec > 10)\n\t\t\tSec = 0;//秒清零 \n\t}\t\n} \n```\n\n方式1初值计算方法,假如定时t(单位:us),使用T0:\n\n则:\n\n```\nTH0 = (65535 - t / 1.085) / 256\nTL0 = (65535 - t / 1.085) % 256\n四舍五入取整后转换为16进制\n```\n\n\n\n\n\n\n# 中断系统\n\n主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断\n\n![image-20210822084113315](https://gitee.com/ajream/images/raw/master/img/20210822084120_image-20210822084113315.png)\n\n引起CPU中断的根源,称为**中断源**。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。\n\n51子系列存在5个中断源:\n**外部中断源2个**\n\n1. INT0——由P3.2端口线引入,低电平或下降沿引起。\n\n2. INT1——由P3.3端口线引入,低电平或下降沿引起。\n\n 这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制\n\n**内部中断源3个**\n\n1. T0——定时器/计数器0中断,由T0回零溢出引起。\n2. T1——定时器/计数器1中断,由T1回零溢出引起。\n3. TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。\n \n 这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。\n\n## 使用中断\n\n如何使用中断,有3大步骤:\n\n### (一)允许产生中断\n\n要允许产生中断首先要配置中断允许寄存器IE和XICON\n\n(**可位寻址**表示每一位都已经用变量定义好了,比如第7位是EA)\n\n![image-20210822091845225](https://gitee.com/ajream/images/raw/master/img/20210822091850_image-20210822091845225.png)\n\n> 总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断\n\n### (二)设置什么时候响应中断\n\n什么时候响应中断由控制寄存器TCON控制\n\n![image-20210822092158946](https://gitee.com/ajream/images/raw/master/img/20210822092202_image-20210822092158946.png)\n\nTFx:当计数值溢出时,就会向cpu发出中断请求,然后**自动**进入对应的**中断处理函数**,然后TFx由硬件自动清0\n\n其它IEx, ITx也差不多是这样\n\n### (三)产生中断后你要干什么——中断处理函数\n\n中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:\n\n格式如下:\n\n```c\nvoid 函数名() interrupt 中断入口号 //中断处理函数,加关键字interrupt和入口号0/1/2/3/4\n{\n //中断处理语句\n}\n\n```\n\n\n\n> 中断优先级:\n>\n> 如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念\n>\n> 中断优先级级别越高,就越先被执行\n>\n> ![image-20210822120231820](https://gitee.com/ajream/images/raw/master/img/20210822120238_image-20210822120231820.png)\n\n\n\n```c\n/*\n定时器0, 工作模式1, 16位定时计数\n数码管动态显示0-10,每隔1秒显示一次\n*/\n\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\nuchar mSec, Sec;//毫秒和秒储存变量\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\nvoid main() //主程序执行按键扫描\n{\t\n\tT0_init();//定时器0初始化\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 120)\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t}\t\n} \n\n//定时器0中断处理函数\nvoid timer0() interrupt 1 //T0中断入口为1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示s\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(二).md","raw":"---\ntitle: 51单片机学习笔记(二)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 定时-计数器、中断系统\nabbrlink: 46cd19c8\ndate: 2021-08-19 08:57:20\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n# 定时计数器\n\n51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)\n\n这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。\n\n当工作在**定时模式**时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。\n可以算出工作在定时模式时最高单次定时时间:65535*1.085us\n\n当工作在**计数器模式**时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1\n\n\n## 使用定时-计数器\n### (一)启动定时计数器\n\nT0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)\n\n![image-20210822095401344](https://gitee.com/ajream/images/raw/master/img/20210822095407_image-20210822095401344.png)\n\n> 总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)\n\n### (二)设定定时计数器的工作模式\n\n这一步由寄存器TMOD(不可位寻址)控制\n\n![image-20210822100638172](https://gitee.com/ajream/images/raw/master/img/20210822100641_image-20210822100638172.png)\n\n第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数\n\n第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式\n\nM1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:\n\n| M1 | M0 | 工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位) |\n| ---- | ---- | ------------------------------------------------------------ |\n| 0 | 0 | 13位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$ |\n| 0 | 1 | 16位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535 |\n| 1 | 0 | 8位自动重装载方式,溢出时自动将THx的值装载到TLx里面 |\n| 1 | 1 | 如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出) |\n\n### (三)查询定时计数器是否溢出\n\n上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0\n\n\n\n```c\n/******************************************************************\n* 【程序功能】: \t定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。\t\n*******************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\n\n//共阴数码管段选表0-9\nuchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tuchar bai, shi, ge;\n\tbai = i / 100; //236 / 100 = 2\n\tshi = i % 100 / 10;\t//236 % 100 / 10 = 3\n\tge = i % 10;//236 % 10 =6\n\t\n\t//第一位数码管 \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFE; //1111 1110\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[bai];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第二位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFD; //1111 1101\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[shi];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n\n\t//第三位数码管\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = 0XFB; //1111 1011\n\tWE = 0;//锁存位选数据\n\t\n\tDU = 1;//打开段选锁存器\n\tP0 = tabel[ge];//\n\tDU = 0;//锁存段选数据\n\tdelay(5);\n}\n\n//定时器T0初始化\nvoid T0_init()\n{\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时器计数模式\n\tTH0 = 0x4b;\n\tTL0 = 0xfd; //定时50ms\n //0x4bfd即19453,共计数65535-19453+1 = 46083次,耗时46083*1.085us = 50 ms\n}\n\nvoid main()//main函数自身会循环\n{\t\n\tuchar mSec, Sec;//毫秒和秒储存变量\n\tT0_init();//定时器T0初始化\n\twhile(1)\n\t{\n\t\tif(TF0 == 1)//判断是否溢出\n\t\t{\n\t\t\tTF0 = 0;//软件清零溢出标志位\n\t\t\tTH0 = 0x4b;\n\t\t\tTL0 = 0xfd; //定时50ms\n\t\t\tmSec++;//50ms到\n\t\t\tif(mSec == 20)\n\t\t\t{\n\t\t\t\tmSec = 0;\n\t\t\t\tSec++;//1秒时间到\n\t\t\t}\t\t\t\t\t\n\t\t}\n\t\tdisplay(Sec); //数码管显示函数\n\t\tif(Sec > 10)\n\t\t\tSec = 0;//秒清零 \n\t}\t\n} \n```\n\n方式1初值计算方法,假如定时t(单位:us),使用T0:\n\n则:\n\n```\nTH0 = (65535 - t / 1.085) / 256\nTL0 = (65535 - t / 1.085) % 256\n四舍五入取整后转换为16进制\n```\n\n\n\n\n\n\n# 中断系统\n\n主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断\n\n![image-20210822084113315](https://gitee.com/ajream/images/raw/master/img/20210822084120_image-20210822084113315.png)\n\n引起CPU中断的根源,称为**中断源**。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。\n\n51子系列存在5个中断源:\n**外部中断源2个**\n\n1. INT0——由P3.2端口线引入,低电平或下降沿引起。\n\n2. INT1——由P3.3端口线引入,低电平或下降沿引起。\n\n 这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制\n\n**内部中断源3个**\n\n1. T0——定时器/计数器0中断,由T0回零溢出引起。\n2. T1——定时器/计数器1中断,由T1回零溢出引起。\n3. TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。\n \n 这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。\n\n## 使用中断\n\n如何使用中断,有3大步骤:\n\n### (一)允许产生中断\n\n要允许产生中断首先要配置中断允许寄存器IE和XICON\n\n(**可位寻址**表示每一位都已经用变量定义好了,比如第7位是EA)\n\n![image-20210822091845225](https://gitee.com/ajream/images/raw/master/img/20210822091850_image-20210822091845225.png)\n\n> 总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断\n\n### (二)设置什么时候响应中断\n\n什么时候响应中断由控制寄存器TCON控制\n\n![image-20210822092158946](https://gitee.com/ajream/images/raw/master/img/20210822092202_image-20210822092158946.png)\n\nTFx:当计数值溢出时,就会向cpu发出中断请求,然后**自动**进入对应的**中断处理函数**,然后TFx由硬件自动清0\n\n其它IEx, ITx也差不多是这样\n\n### (三)产生中断后你要干什么——中断处理函数\n\n中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:\n\n格式如下:\n\n```c\nvoid 函数名() interrupt 中断入口号 //中断处理函数,加关键字interrupt和入口号0/1/2/3/4\n{\n //中断处理语句\n}\n\n```\n\n\n\n> 中断优先级:\n>\n> 如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念\n>\n> 中断优先级级别越高,就越先被执行\n>\n> ![image-20210822120231820](https://gitee.com/ajream/images/raw/master/img/20210822120238_image-20210822120231820.png)\n\n\n\n```c\n/*\n定时器0, 工作模式1, 16位定时计数\n数码管动态显示0-10,每隔1秒显示一次\n*/\n\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit key_s2 = P3^0;//独立按键S2\nsbit key_s3 = P3^1;//独立按键S3\nuchar num;//数码管显示的值\nuchar mSec, Sec;//毫秒和秒储存变量\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid T0_init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD = 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n\nvoid main() //主程序执行按键扫描\n{\t\n\tT0_init();//定时器0初始化\n\twhile(1)\n\t{\n\t\tif(key_s2 == 0)//判断S2是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s2 == 0)\n\t\t\t{\n\t\t\t\tif(num != 120)\n\t\t\t\tnum++;\n\t\t\t\twhile(!key_s2);//松手检测\n\t\t\t}\t\n\t\t}\n\t\tif(key_s3 == 0)//判断S3是否被按下\n\t\t{\n\t\t\tdelay(20);//按键消抖\n\t\t\tif(key_s3 == 0)\n\t\t\t{\n\t\t\t\tif(num > 0)\n\t\t\t\t\tnum--;\n\t\t\t\twhile(!key_s3);//松手检测\n\t\t\t}\t\n\t\t}\n\t}\t\n} \n\n//定时器0中断处理函数\nvoid timer0() interrupt 1 //T0中断入口为1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示s\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(二)","published":1,"updated":"2021-09-10T12:45:06.012Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o6000bakve2lmd5swc","content":"

定时计数器

51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)

\n

这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。

\n

当工作在定时模式时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。
可以算出工作在定时模式时最高单次定时时间:65535*1.085us

\n

当工作在计数器模式时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1

\n

使用定时-计数器

(一)启动定时计数器

T0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)

\n

\"image-20210822095401344\"

\n
\n

总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)

\n
\n

(二)设定定时计数器的工作模式

这一步由寄存器TMOD(不可位寻址)控制

\n

\"image-20210822100638172\"

\n

第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数

\n

第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式

\n

M1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
M1M0工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位)
0013位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$
0116位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535
108位自动重装载方式,溢出时自动将THx的值装载到TLx里面
11如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出)
\n
\n

(三)查询定时计数器是否溢出

上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/******************************************************************
* 【程序功能】: \t定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。\t
*******************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void display(uchar i)
{
\tuchar bai, shi, ge;
\tbai = i / 100; //236 / 100 = 2
\tshi = i % 100 / 10;\t//236 % 100 / 10 = 3
\tge = i % 10;//236 % 10 =6
\t
\t//第一位数码管 \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[bai];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第二位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFD; //1111 1101
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[shi];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第三位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFB; //1111 1011
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[ge];//
\tDU = 0;//锁存段选数据
\tdelay(5);
}

//定时器T0初始化
void T0_init()
{
\tTR0 = 1;\t //启动定时器0
\tTMOD = 0X01; //定时器工作模式1,16位定时器计数模式
\tTH0 = 0x4b;
\tTL0 = 0xfd; //定时50ms
//0x4bfd即19453,共计数65535-19453+1 = 46083次,耗时46083*1.085us = 50 ms
}

void main()//main函数自身会循环
{\t
\tuchar mSec, Sec;//毫秒和秒储存变量
\tT0_init();//定时器T0初始化
\twhile(1)
\t{
\t\tif(TF0 == 1)//判断是否溢出
\t\t{
\t\t\tTF0 = 0;//软件清零溢出标志位
\t\t\tTH0 = 0x4b;
\t\t\tTL0 = 0xfd; //定时50ms
\t\t\tmSec++;//50ms到
\t\t\tif(mSec == 20)
\t\t\t{
\t\t\t\tmSec = 0;
\t\t\t\tSec++;//1秒时间到
\t\t\t}\t\t\t\t\t
\t\t}
\t\tdisplay(Sec); //数码管显示函数
\t\tif(Sec > 10)
\t\t\tSec = 0;//秒清零
\t}\t
}
\n

方式1初值计算方法,假如定时t(单位:us),使用T0:

\n

则:

\n
1
2
3
TH0 = (65535 - t / 1.085) / 256
TL0 = (65535 - t / 1.085) % 256
四舍五入取整后转换为16进制
\n

中断系统

主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断

\n

\"image-20210822084113315\"

\n

引起CPU中断的根源,称为中断源。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。

\n

51子系列存在5个中断源:
外部中断源2个

\n
    \n
  1. INT0——由P3.2端口线引入,低电平或下降沿引起。

    \n
  2. \n
  3. INT1——由P3.3端口线引入,低电平或下降沿引起。

    \n

    这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制

    \n
  4. \n
\n

内部中断源3个

\n
    \n
  1. T0——定时器/计数器0中断,由T0回零溢出引起。
  2. \n
  3. T1——定时器/计数器1中断,由T1回零溢出引起。
  4. \n
  5. TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。

    \n

    这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。

    \n
  6. \n
\n

使用中断

如何使用中断,有3大步骤:

\n

(一)允许产生中断

要允许产生中断首先要配置中断允许寄存器IE和XICON

\n

(可位寻址表示每一位都已经用变量定义好了,比如第7位是EA)

\n

\"image-20210822091845225\"

\n
\n

总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断

\n
\n

(二)设置什么时候响应中断

什么时候响应中断由控制寄存器TCON控制

\n

\"image-20210822092158946\"

\n

TFx:当计数值溢出时,就会向cpu发出中断请求,然后自动进入对应的中断处理函数,然后TFx由硬件自动清0

\n

其它IEx, ITx也差不多是这样

\n

(三)产生中断后你要干什么——中断处理函数

中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:

\n

格式如下:

\n
1
2
3
4
5
void 函数名() interrupt 中断入口号  //中断处理函数,加关键字interrupt和入口号0/1/2/3/4
{
//中断处理语句
}

\n
\n

中断优先级:

\n

如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念

\n

中断优先级级别越高,就越先被执行

\n

\"image-20210822120231820\"

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*
定时器0, 工作模式1, 16位定时计数
数码管动态显示0-10,每隔1秒显示一次
*/

#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值
uchar mSec, Sec;//毫秒和秒储存变量

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void T0_init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD = 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}

void main() //主程序执行按键扫描
{\t
\tT0_init();//定时器0初始化
\twhile(1)
\t{
\t\tif(key_s2 == 0)//判断S2是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s2 == 0)
\t\t\t{
\t\t\t\tif(num != 120)
\t\t\t\tnum++;
\t\t\t\twhile(!key_s2);//松手检测
\t\t\t}\t
\t\t}
\t\tif(key_s3 == 0)//判断S3是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s3 == 0)
\t\t\t{
\t\t\t\tif(num > 0)
\t\t\t\t\tnum--;
\t\t\t\twhile(!key_s3);//松手检测
\t\t\t}\t
\t\t}
\t}\t
}

//定时器0中断处理函数
void timer0() interrupt 1 //T0中断入口为1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示s
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

定时计数器

51单片机有2个16位定时器/计数器:定时器T0(T0为P3.4)和定时器T1(T1为P3.5)

\n

这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。

\n

当工作在定时模式时,每经过一个机器周期(约1.085us)内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。
可以算出工作在定时模式时最高单次定时时间:65535*1.085us

\n

当工作在计数器模式时,T0(P3.4引脚)或T1(P3.5引脚)每来一个脉冲计数寄存器加1

\n

使用定时-计数器

(一)启动定时计数器

T0/T1的启动由控制寄存器是TCON(可位寻址)控制,如下图(IE1/IE0/IT1/IT0先不用看)

\n

\"image-20210822095401344\"

\n
\n

总结:TRx用来启动定时计数器Tx,TFx用来判断什么时候溢出(Tx表示是T0还是T1)

\n
\n

(二)设定定时计数器的工作模式

这一步由寄存器TMOD(不可位寻址)控制

\n

\"image-20210822100638172\"

\n

第7位和第3位(GATE位):置1时只有外部引脚INT1/INT0为高电平、且开启了定时计数器(TRx=1)时,定时计数器才会开始计数

\n

第6位和第2位(C/T位):为1时表示工作在计数模式(外部来一个脉冲就+1),为0表示工作在定时模式

\n

M1/M0位:用于选择定时计数器是计数方式(前面说过,这是16位定时计数器,但不一定全部16位都用,可以只用其中几位),有4种方式:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
M1M0工作方式及说明(TLx、THx分别表示Tx定时计数器【共16位】的低8位、高8位)
0013位计数方式,TLx用低5位,THx的8位全用,总共13位,可以从0计数到:$2^{13}-1=8191$
0116位计数方式,即TLx的8位,THx的8位全部使用,计数值0~65535
108位自动重装载方式,溢出时自动将THx的值装载到TLx里面
11如果是T1此时该定时计数器失效;如果是T0,这时T0的高8位TH0作为T1使用(由TR1、TF1启动、判断溢出),但只能计数到($2^8-1=255$),TL0还是自己(T0)使用(由TR0、TF0启动、判断溢出)
\n
\n

(三)查询定时计数器是否溢出

上面(第一点):TFx溢出时会被硬件自动置为1, 但前提是使用了中断函数,否则可以用程序清0

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/******************************************************************
* 【程序功能】: \t定时器0工作模式1 16位定时模式,数码管动态显示0-10,秒表。\t
*******************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选

//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void display(uchar i)
{
\tuchar bai, shi, ge;
\tbai = i / 100; //236 / 100 = 2
\tshi = i % 100 / 10;\t//236 % 100 / 10 = 3
\tge = i % 10;//236 % 10 =6
\t
\t//第一位数码管 \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFE; //1111 1110
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[bai];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第二位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFD; //1111 1101
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[shi];//
\tDU = 0;//锁存段选数据
\tdelay(5);

\t//第三位数码管
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = 0XFB; //1111 1011
\tWE = 0;//锁存位选数据
\t
\tDU = 1;//打开段选锁存器
\tP0 = tabel[ge];//
\tDU = 0;//锁存段选数据
\tdelay(5);
}

//定时器T0初始化
void T0_init()
{
\tTR0 = 1;\t //启动定时器0
\tTMOD = 0X01; //定时器工作模式1,16位定时器计数模式
\tTH0 = 0x4b;
\tTL0 = 0xfd; //定时50ms
//0x4bfd即19453,共计数65535-19453+1 = 46083次,耗时46083*1.085us = 50 ms
}

void main()//main函数自身会循环
{\t
\tuchar mSec, Sec;//毫秒和秒储存变量
\tT0_init();//定时器T0初始化
\twhile(1)
\t{
\t\tif(TF0 == 1)//判断是否溢出
\t\t{
\t\t\tTF0 = 0;//软件清零溢出标志位
\t\t\tTH0 = 0x4b;
\t\t\tTL0 = 0xfd; //定时50ms
\t\t\tmSec++;//50ms到
\t\t\tif(mSec == 20)
\t\t\t{
\t\t\t\tmSec = 0;
\t\t\t\tSec++;//1秒时间到
\t\t\t}\t\t\t\t\t
\t\t}
\t\tdisplay(Sec); //数码管显示函数
\t\tif(Sec > 10)
\t\t\tSec = 0;//秒清零
\t}\t
}
\n

方式1初值计算方法,假如定时t(单位:us),使用T0:

\n

则:

\n
1
2
3
TH0 = (65535 - t / 1.085) / 256
TL0 = (65535 - t / 1.085) % 256
四舍五入取整后转换为16进制
\n

中断系统

主程序正在执行某一事件,突然发生一个紧急事件,需要中断当前正在执行的程序,去处理这一紧急事件,这就是中断

\n

\"image-20210822084113315\"

\n

引起CPU中断的根源,称为中断源。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。

\n

51子系列存在5个中断源:
外部中断源2个

\n
    \n
  1. INT0——由P3.2端口线引入,低电平或下降沿引起。

    \n
  2. \n
  3. INT1——由P3.3端口线引入,低电平或下降沿引起。

    \n

    这两个外部中断源标志和它们的触发方式控制位由特殊功能寄存器TCON的低4位控制

    \n
  4. \n
\n

内部中断源3个

\n
    \n
  1. T0——定时器/计数器0中断,由T0回零溢出引起。
  2. \n
  3. T1——定时器/计数器1中断,由T1回零溢出引起。
  4. \n
  5. TI/RI——串行I/O中断,串行端口完成一帧字符发送/接收后引起。

    \n

    这3个内部中断源的控制位分别锁存在特殊功能寄存器TCON和SCON中。

    \n
  6. \n
\n

使用中断

如何使用中断,有3大步骤:

\n

(一)允许产生中断

要允许产生中断首先要配置中断允许寄存器IE和XICON

\n

(可位寻址表示每一位都已经用变量定义好了,比如第7位是EA)

\n

\"image-20210822091845225\"

\n
\n

总结:要允许中断,必须先开启总中断EA=1,再开启对应模块的中断

\n
\n

(二)设置什么时候响应中断

什么时候响应中断由控制寄存器TCON控制

\n

\"image-20210822092158946\"

\n

TFx:当计数值溢出时,就会向cpu发出中断请求,然后自动进入对应的中断处理函数,然后TFx由硬件自动清0

\n

其它IEx, ITx也差不多是这样

\n

(三)产生中断后你要干什么——中断处理函数

中断处理函数的功能就是当产生中断后你要干什么,这个函数有格式要求,不需要主动调用,由cpu响应中断时自动调用:

\n

格式如下:

\n
1
2
3
4
5
void 函数名() interrupt 中断入口号  //中断处理函数,加关键字interrupt和入口号0/1/2/3/4
{
//中断处理语句
}

\n
\n

中断优先级:

\n

如果有多个中断同时产生,该调用哪个中断处理函数呢,因此引出了中断优先级这个概念

\n

中断优先级级别越高,就越先被执行

\n

\"image-20210822120231820\"

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*
定时器0, 工作模式1, 16位定时计数
数码管动态显示0-10,每隔1秒显示一次
*/

#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值
uchar mSec, Sec;//毫秒和秒储存变量

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void T0_init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD = 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}

void main() //主程序执行按键扫描
{\t
\tT0_init();//定时器0初始化
\twhile(1)
\t{
\t\tif(key_s2 == 0)//判断S2是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s2 == 0)
\t\t\t{
\t\t\t\tif(num != 120)
\t\t\t\tnum++;
\t\t\t\twhile(!key_s2);//松手检测
\t\t\t}\t
\t\t}
\t\tif(key_s3 == 0)//判断S3是否被按下
\t\t{
\t\t\tdelay(20);//按键消抖
\t\t\tif(key_s3 == 0)
\t\t\t{
\t\t\t\tif(num > 0)
\t\t\t\t\tnum--;
\t\t\t\twhile(!key_s3);//松手检测
\t\t\t}\t
\t\t}
\t}\t
}

//定时器0中断处理函数
void timer0() interrupt 1 //T0中断入口为1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示s
}
\n"},{"title":"51单片机学习笔记(八)","description":"DS1302日历时钟芯片","abbrlink":"b836e65","date":"2021-08-20T09:32:44.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n## DS1302\n\n### 芯片功能\n\nDS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。\n\nDS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便\n\n工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。\n\nDS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。\n\n\n\n### 引脚\n\n\n\n![image-20210824170629438](https://gitee.com/ajream/images/raw/master/img/20210824170630_image-20210824170629438.png)\n\n\n\n- VCC1:主电源\n\n- VCC2:备用电源\n\n 当VCC2大于VCC1+0.2V时,由VCC2向DS1302供电否则由VCC1向DS1302供电\n\n- SCLK:串行时钟输入端,控制数据输入与输出\n\n- I/O :双向输入线\n\n- CE:使能端,CE为高时允许读写DS1302数据,为低时禁止读写\n\n\n\n\n\n### 应用电路\n\n开发板实际电路如下:\n\n![image-20210824170855223](https://gitee.com/ajream/images/raw/master/img/20210824170856_image-20210824170855223.png)\n\n说明:\n\nDS1302和单片机的IO连接只需3条线:\n\n- CE数据传输使能端、SCLK串行时钟输入端、I/O串行数据端\n\n (开发板上把这3个引脚分别接到了j5排针上同时通过4.7K电阻上拉到VCC,我们实际使用DS1302芯片时需要用杜邦线把j5和相应的单片机IO链接上)\n\n此外\n\n- X1、X2晶振引脚外接32.768KHZ圆形晶振,给时钟芯片提供工作频率\n\n- VCC2接的开发上的系统电源,VCC1接的预留电池座\n\n- DS1302第4脚接的系统GND\n\n\n\n### DS1302的寄存器\n\n\n\n\n\n![image-20210824174037791](https://gitee.com/ajream/images/raw/master/img/20210824174040_image-20210824174037791.png)\n\n\n\n如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 `0001 0000`)\n\n\n\n- 秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。\n\n- 小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。\n\n- 写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。\n\n \n\n使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令`81h`,读取分,用 `83h`, ......\n\n\n\n\n\n\n\n突发模式:\n\n突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。\n\n突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:\n\n![image-20210824174700324](https://gitee.com/ajream/images/raw/master/img/20210824174701_image-20210824174700324.png)\n\n也就是说,如果发送`BFh`指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间\n\n\n\n\n\n### DS1302时序\n\n\n\nDS1302读写数据时序,数据的传输是从最低位开始(BIT0)。\n\n数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。\n\n\n\n**读数据:**CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。\n\n![image-20210824175336808](https://gitee.com/ajream/images/raw/master/img/20210824175338_image-20210824175336808.png)\n\n\n\n**写数据:**CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。\n\n![image-20210824175358215](https://gitee.com/ajream/images/raw/master/img/20210824175359_image-20210824175358215.png)\n\n\n\n\n\n\n\n## 例子\n\n数码管显示DS1302时钟\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit TSCLK = P1^0;//时钟线 接到P10上用杜邦线\nsbit TIO = P1^1;//数据线,接到P11上\nsbit TRST = P1^2;//使能端,接到P12上\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n\nvoid Display(uchar Hour, Min, Sec)\n{\t\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Hour/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[0];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Hour%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Min/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[2];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Min%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[3];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Sec/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[4];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Sec%10]; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[5];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n\n}\n\n//写DS1302数据\nvoid Write_DS1302_DAT(uchar cmd, uchar dat)\n{\n\tuchar i;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0;//拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = dat & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tdat >>= 1;\t\t //右移一位\n\t}\n}\n//读DS1302数据\nuchar Read_DS1302_DAT(uchar cmd)\n{\n\tuchar i, dat;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0; //拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01;//写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次读1位,读8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线,产生下降沿,DS1302把数据放到TIO上\n\t\tdat >>= 1;\t\t //右移一位\n\t\tif(TIO)\tdat |= 0x80;//读取数据,从最低位开始\n\t\tTSCLK = 1;\t\t\t//拉高时钟总线,以备下一次产生下降沿\n\t}\n\treturn dat;\t//返回读出数据\n}\n\n//数据转BCD码\nuchar Dat_Chg_BCD(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 10;\n\tdat2 = dat % 10;\n\tdat2 = dat2 + dat1 * 16;\n\treturn dat2;\n}\n\n//BCD码转换为数据\nuchar BCD_Chg_Dat(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 16;\n\tdat2 = dat % 16;\n\tdat2 = dat2 + dat1 * 10;\n\treturn dat2;\n}\n\nvoid main()\n{\n\tuchar i;\n\tuchar Sec, Min, Hour;\n\tWrite_DS1302_DAT(0x8e, 0);//清除写保护\n\tWrite_DS1302_DAT(0x80, Dat_Chg_BCD(30));//30秒(并且进行BCD码转换)\n\tWrite_DS1302_DAT(0x82, Dat_Chg_BCD(15));//15分\n\tWrite_DS1302_DAT(0x84, Dat_Chg_BCD(19));//19时\n\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\twhile(1)\n\t{\n\t\tWrite_DS1302_DAT(0x8e, 0); //清除写保护\n\t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));//读秒寄存器(并且进行BCD码转换)\n\t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83));//读分寄存器\n\t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));//读时寄存器\n\t\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\t\tfor(i = 0; i < 50; i++)\t//循环显示时钟\n\t\t\tDisplay(Hour, Min, Sec);\n\n\t}\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(八).md","raw":"---\ntitle: 51单片机学习笔记(八)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: DS1302日历时钟芯片\nabbrlink: b836e65\ndate: 2021-08-20 17:32:44\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n## DS1302\n\n### 芯片功能\n\nDS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。\n\nDS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便\n\n工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。\n\nDS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。\n\n\n\n### 引脚\n\n\n\n![image-20210824170629438](https://gitee.com/ajream/images/raw/master/img/20210824170630_image-20210824170629438.png)\n\n\n\n- VCC1:主电源\n\n- VCC2:备用电源\n\n 当VCC2大于VCC1+0.2V时,由VCC2向DS1302供电否则由VCC1向DS1302供电\n\n- SCLK:串行时钟输入端,控制数据输入与输出\n\n- I/O :双向输入线\n\n- CE:使能端,CE为高时允许读写DS1302数据,为低时禁止读写\n\n\n\n\n\n### 应用电路\n\n开发板实际电路如下:\n\n![image-20210824170855223](https://gitee.com/ajream/images/raw/master/img/20210824170856_image-20210824170855223.png)\n\n说明:\n\nDS1302和单片机的IO连接只需3条线:\n\n- CE数据传输使能端、SCLK串行时钟输入端、I/O串行数据端\n\n (开发板上把这3个引脚分别接到了j5排针上同时通过4.7K电阻上拉到VCC,我们实际使用DS1302芯片时需要用杜邦线把j5和相应的单片机IO链接上)\n\n此外\n\n- X1、X2晶振引脚外接32.768KHZ圆形晶振,给时钟芯片提供工作频率\n\n- VCC2接的开发上的系统电源,VCC1接的预留电池座\n\n- DS1302第4脚接的系统GND\n\n\n\n### DS1302的寄存器\n\n\n\n\n\n![image-20210824174037791](https://gitee.com/ajream/images/raw/master/img/20210824174040_image-20210824174037791.png)\n\n\n\n如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 `0001 0000`)\n\n\n\n- 秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。\n\n- 小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。\n\n- 写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。\n\n \n\n使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令`81h`,读取分,用 `83h`, ......\n\n\n\n\n\n\n\n突发模式:\n\n突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。\n\n突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:\n\n![image-20210824174700324](https://gitee.com/ajream/images/raw/master/img/20210824174701_image-20210824174700324.png)\n\n也就是说,如果发送`BFh`指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间\n\n\n\n\n\n### DS1302时序\n\n\n\nDS1302读写数据时序,数据的传输是从最低位开始(BIT0)。\n\n数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。\n\n\n\n**读数据:**CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。\n\n![image-20210824175336808](https://gitee.com/ajream/images/raw/master/img/20210824175338_image-20210824175336808.png)\n\n\n\n**写数据:**CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。\n\n![image-20210824175358215](https://gitee.com/ajream/images/raw/master/img/20210824175359_image-20210824175358215.png)\n\n\n\n\n\n\n\n## 例子\n\n数码管显示DS1302时钟\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit TSCLK = P1^0;//时钟线 接到P10上用杜邦线\nsbit TIO = P1^1;//数据线,接到P11上\nsbit TRST = P1^2;//使能端,接到P12上\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n\nvoid Display(uchar Hour, Min, Sec)\n{\t\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Hour/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[0];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Hour%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Min/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[2];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Min%10]|0x80; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[3];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t\n\tP0 = table[Sec/10];\t\t\n\tDU = 1;\t\t\t\t\t\t\n\tDU = 0;\t\t\t\t\t\t\t\n\n\tWE = 0;\t\t\t\t\t\t\n\tP0 = T_COM[4];\t\t\t\t \n\tWE = 1;\t\t\t\t\t\t\n\tWE = 0;\t\t\t\t\t\t\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Sec%10]; \n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[5];\t\t\t \n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n\n}\n\n//写DS1302数据\nvoid Write_DS1302_DAT(uchar cmd, uchar dat)\n{\n\tuchar i;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0;//拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = dat & 0x01; //写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tdat >>= 1;\t\t //右移一位\n\t}\n}\n//读DS1302数据\nuchar Read_DS1302_DAT(uchar cmd)\n{\n\tuchar i, dat;\n\tTRST = 0; //拉低使能端\n\tTSCLK = 0; //拉低数据总线\n\tTRST = 1; //拉高使能端,产生上升沿开始写数据\n\tfor(i = 0; i < 8; i++)//每次写1位,写8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线\n\t\tTIO = cmd & 0x01;//写1位数据,从最低位开始写\n\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走\n\t\tcmd >>=1;\t\t //右移一位\n\t}\n\tfor(i = 0; i < 8; i++)//每次读1位,读8次\n\t{\n\t\tTSCLK = 0;\t\t //拉低时钟总线,产生下降沿,DS1302把数据放到TIO上\n\t\tdat >>= 1;\t\t //右移一位\n\t\tif(TIO)\tdat |= 0x80;//读取数据,从最低位开始\n\t\tTSCLK = 1;\t\t\t//拉高时钟总线,以备下一次产生下降沿\n\t}\n\treturn dat;\t//返回读出数据\n}\n\n//数据转BCD码\nuchar Dat_Chg_BCD(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 10;\n\tdat2 = dat % 10;\n\tdat2 = dat2 + dat1 * 16;\n\treturn dat2;\n}\n\n//BCD码转换为数据\nuchar BCD_Chg_Dat(uchar dat)\n{\n\tuchar dat1, dat2;\n\tdat1 = dat / 16;\n\tdat2 = dat % 16;\n\tdat2 = dat2 + dat1 * 10;\n\treturn dat2;\n}\n\nvoid main()\n{\n\tuchar i;\n\tuchar Sec, Min, Hour;\n\tWrite_DS1302_DAT(0x8e, 0);//清除写保护\n\tWrite_DS1302_DAT(0x80, Dat_Chg_BCD(30));//30秒(并且进行BCD码转换)\n\tWrite_DS1302_DAT(0x82, Dat_Chg_BCD(15));//15分\n\tWrite_DS1302_DAT(0x84, Dat_Chg_BCD(19));//19时\n\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\twhile(1)\n\t{\n\t\tWrite_DS1302_DAT(0x8e, 0); //清除写保护\n\t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));//读秒寄存器(并且进行BCD码转换)\n\t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83));//读分寄存器\n\t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));//读时寄存器\n\t\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护\n\t\tfor(i = 0; i < 50; i++)\t//循环显示时钟\n\t\t\tDisplay(Hour, Min, Sec);\n\n\t}\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(八)","published":1,"updated":"2021-09-10T12:44:55.652Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6o9000gakve5fnphajo","content":"

DS1302

芯片功能

DS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。

\n

DS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便

\n

工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。

\n

DS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。

\n

引脚

\"image-20210824170629438\"

\n
    \n
  • VCC1:主电源

    \n
  • \n
  • VCC2:备用电源

    \n

    当VCC2大于VCC1+0.2V时,由VCC2向DS1302供电否则由VCC1向DS1302供电

    \n
  • \n
  • SCLK:串行时钟输入端,控制数据输入与输出

    \n
  • \n
  • I/O :双向输入线

    \n
  • \n
  • CE:使能端,CE为高时允许读写DS1302数据,为低时禁止读写

    \n
  • \n
\n

应用电路

开发板实际电路如下:

\n

\"image-20210824170855223\"

\n

说明:

\n

DS1302和单片机的IO连接只需3条线:

\n
    \n
  • CE数据传输使能端、SCLK串行时钟输入端、I/O串行数据端

    \n

    (开发板上把这3个引脚分别接到了j5排针上同时通过4.7K电阻上拉到VCC,我们实际使用DS1302芯片时需要用杜邦线把j5和相应的单片机IO链接上)

    \n
  • \n
\n

此外

\n
    \n
  • X1、X2晶振引脚外接32.768KHZ圆形晶振,给时钟芯片提供工作频率

    \n
  • \n
  • VCC2接的开发上的系统电源,VCC1接的预留电池座

    \n
  • \n
  • DS1302第4脚接的系统GND

    \n
  • \n
\n

DS1302的寄存器

\"image-20210824174037791\"

\n

如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 0001 0000

\n
    \n
  • 秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。

    \n
  • \n
  • 小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。

    \n
  • \n
  • 写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。

    \n
  • \n
\n

使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令81h,读取分,用 83h, ……

\n

突发模式:

\n

突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。

\n

突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:

\n

\"image-20210824174700324\"

\n

也就是说,如果发送BFh指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间

\n

DS1302时序

DS1302读写数据时序,数据的传输是从最低位开始(BIT0)。

\n

数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。

\n

读数据:CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。

\n

\"image-20210824175336808\"

\n

写数据:CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。

\n

\"image-20210824175358215\"

\n

例子

数码管显示DS1302时钟

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <reg52.h>
#include <intrins.h>
#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit TSCLK = P1^0;//时钟线 接到P10上用杜邦线
sbit TIO = P1^1;//数据线,接到P11上
sbit TRST = P1^2;//使能端,接到P12上
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
/*====================================
共阴极数码管段选码
====================================*/
uchar code table[]={
//0\t\t1\t 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
};

/*====================================
数码管位选码
====================================*/
\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数:void Delay_Ms(INT16U ms)
参数:ms,毫秒延时形参
描述:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void Delay_Ms(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}

void Display(uchar Hour, Min, Sec)
{\t\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Hour/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[0];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Hour%10]|0x80;
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[1];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Min/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[2];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Min%10]|0x80;
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[3];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Sec/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[4];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Sec%10];
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[5];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);

}

//写DS1302数据
void Write_DS1302_DAT(uchar cmd, uchar dat)
{
\tuchar i;
\tTRST = 0; //拉低使能端
\tTSCLK = 0;//拉低数据总线
\tTRST = 1; //拉高使能端,产生上升沿开始写数据
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = cmd & 0x01; //写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tcmd >>=1;\t\t //右移一位
\t}
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = dat & 0x01; //写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tdat >>= 1;\t\t //右移一位
\t}
}
//读DS1302数据
uchar Read_DS1302_DAT(uchar cmd)
{
\tuchar i, dat;
\tTRST = 0; //拉低使能端
\tTSCLK = 0; //拉低数据总线
\tTRST = 1; //拉高使能端,产生上升沿开始写数据
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = cmd & 0x01;//写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tcmd >>=1;\t\t //右移一位
\t}
\tfor(i = 0; i < 8; i++)//每次读1位,读8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线,产生下降沿,DS1302把数据放到TIO上
\t\tdat >>= 1;\t\t //右移一位
\t\tif(TIO)\tdat |= 0x80;//读取数据,从最低位开始
\t\tTSCLK = 1;\t\t\t//拉高时钟总线,以备下一次产生下降沿
\t}
\treturn dat;\t//返回读出数据
}

//数据转BCD码
uchar Dat_Chg_BCD(uchar dat)
{
\tuchar dat1, dat2;
\tdat1 = dat / 10;
\tdat2 = dat % 10;
\tdat2 = dat2 + dat1 * 16;
\treturn dat2;
}

//BCD码转换为数据
uchar BCD_Chg_Dat(uchar dat)
{
\tuchar dat1, dat2;
\tdat1 = dat / 16;
\tdat2 = dat % 16;
\tdat2 = dat2 + dat1 * 10;
\treturn dat2;
}

void main()
{
\tuchar i;
\tuchar Sec, Min, Hour;
\tWrite_DS1302_DAT(0x8e, 0);//清除写保护
\tWrite_DS1302_DAT(0x80, Dat_Chg_BCD(30));//30秒(并且进行BCD码转换)
\tWrite_DS1302_DAT(0x82, Dat_Chg_BCD(15));//15分
\tWrite_DS1302_DAT(0x84, Dat_Chg_BCD(19));//19时
\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护
\twhile(1)
\t{
\t\tWrite_DS1302_DAT(0x8e, 0); //清除写保护
\t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));//读秒寄存器(并且进行BCD码转换)
\t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83));//读分寄存器
\t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));//读时寄存器
\t\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护
\t\tfor(i = 0; i < 50; i++)\t//循环显示时钟
\t\t\tDisplay(Hour, Min, Sec);

\t}
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

DS1302

芯片功能

DS1302是美国DALLAS推出的一款高性能、低功耗的日历时钟芯片。

\n

DS1302是一种串行接口的实时时钟,芯片内部具有可编程的日历时钟和31个字节的静态RAM,日历时钟可以自动进行闰年补偿,计时准确,接口简单,使用方便

\n

工作电压范围2.5~5.5V,芯片自身还具有对备用电池进行涓流充电功能,可有效延长备用电池的使用寿命。

\n

DS1302用于数据记录,能实现数据与该数据出现的时间同时记录,广泛应用于测量系统中。

\n

引脚

\"image-20210824170629438\"

\n
    \n
  • VCC1:主电源

    \n
  • \n
  • VCC2:备用电源

    \n

    当VCC2大于VCC1+0.2V时,由VCC2向DS1302供电否则由VCC1向DS1302供电

    \n
  • \n
  • SCLK:串行时钟输入端,控制数据输入与输出

    \n
  • \n
  • I/O :双向输入线

    \n
  • \n
  • CE:使能端,CE为高时允许读写DS1302数据,为低时禁止读写

    \n
  • \n
\n

应用电路

开发板实际电路如下:

\n

\"image-20210824170855223\"

\n

说明:

\n

DS1302和单片机的IO连接只需3条线:

\n
    \n
  • CE数据传输使能端、SCLK串行时钟输入端、I/O串行数据端

    \n

    (开发板上把这3个引脚分别接到了j5排针上同时通过4.7K电阻上拉到VCC,我们实际使用DS1302芯片时需要用杜邦线把j5和相应的单片机IO链接上)

    \n
  • \n
\n

此外

\n
    \n
  • X1、X2晶振引脚外接32.768KHZ圆形晶振,给时钟芯片提供工作频率

    \n
  • \n
  • VCC2接的开发上的系统电源,VCC1接的预留电池座

    \n
  • \n
  • DS1302第4脚接的系统GND

    \n
  • \n
\n

DS1302的寄存器

\"image-20210824174037791\"

\n

如图所示,时钟日历包含在 7 个读/写寄存器内,读/写寄存器中的数据是 BCD 码。(4位BCD码最大表示9,不是15, 例如用BCD码表示10为 0001 0000

\n
    \n
  • 秒寄存器(81h、80h)的BIT7定义为时钟暂停标志(CH)。当该位置为1时,时钟振荡器停止,DS1302处于低功耗状态;当该位置为0时,时钟开始运行。

    \n
  • \n
  • 小时寄存器(85h、84h)的BIT7用于定义DS1302是运行于12小时模式还是24小时模式,当为1时,选择12小时模式,此时BIT5为AM/PM位,在24小时模式时此位为小时数据位。

    \n
  • \n
  • 写保护寄存器(8Fh、8Eh)的BIT7是写保护位(WP),其它7位均为0。在任何对时钟或RAM读写操作之前,WP位必须为0。当WP位为1时,不能对任何时钟日历寄存器或RAM进行写操作。

    \n
  • \n
\n

使用对应指令即可读取或写入数据到相应寄存器,比如读取秒,用指令81h,读取分,用 83h, ……

\n

突发模式:

\n

突发模式可以指定任 何的时钟/日历或者 RAM 寄存器为突发模式,和以前一样,第 6 位指定时钟或 RAM 而 0 位指定读或写。

\n

突发模式的实质是指一次传送多个字节的时钟信号和 RAM 数据。如下图所示:

\n

\"image-20210824174700324\"

\n

也就是说,如果发送BFh指令,DS1302会一次性把年月周日、时分秒发送过来,不需要像前面一个个指令来获取时间

\n

DS1302时序

DS1302读写数据时序,数据的传输是从最低位开始(BIT0)。

\n

数据是以位(BIT)为单位依次写入或读出,读写数据操作中SCLK上升沿时执行写入数据,下降沿时执行读出数据。

\n

读数据:CE端从低到高的一个上升沿开始允许开始读数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期读出数据。

\n

\"image-20210824175336808\"

\n

写数据:CE端从低到高的一个上升沿开始允许开始写数据,拉低CE端则禁止读写数据;开始的8个SCLK周期,写命令字节,数据的后8个SCLK 周期写入数据。

\n

\"image-20210824175358215\"

\n

例子

数码管显示DS1302时钟

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <reg52.h>
#include <intrins.h>
#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit TSCLK = P1^0;//时钟线 接到P10上用杜邦线
sbit TIO = P1^1;//数据线,接到P11上
sbit TRST = P1^2;//使能端,接到P12上
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
/*====================================
共阴极数码管段选码
====================================*/
uchar code table[]={
//0\t\t1\t 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
};

/*====================================
数码管位选码
====================================*/
\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数:void Delay_Ms(INT16U ms)
参数:ms,毫秒延时形参
描述:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void Delay_Ms(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}

void Display(uchar Hour, Min, Sec)
{\t\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Hour/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[0];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Hour%10]|0x80;
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[1];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Min/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[2];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Min%10]|0x80;
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[3];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t
\tP0 = table[Sec/10];\t\t
\tDU = 1;\t\t\t\t\t\t
\tDU = 0;\t\t\t\t\t\t\t

\tWE = 0;\t\t\t\t\t\t
\tP0 = T_COM[4];\t\t\t\t
\tWE = 1;\t\t\t\t\t\t
\tWE = 0;\t\t\t\t\t\t
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Sec%10];
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[5];\t\t\t
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);

}

//写DS1302数据
void Write_DS1302_DAT(uchar cmd, uchar dat)
{
\tuchar i;
\tTRST = 0; //拉低使能端
\tTSCLK = 0;//拉低数据总线
\tTRST = 1; //拉高使能端,产生上升沿开始写数据
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = cmd & 0x01; //写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tcmd >>=1;\t\t //右移一位
\t}
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = dat & 0x01; //写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tdat >>= 1;\t\t //右移一位
\t}
}
//读DS1302数据
uchar Read_DS1302_DAT(uchar cmd)
{
\tuchar i, dat;
\tTRST = 0; //拉低使能端
\tTSCLK = 0; //拉低数据总线
\tTRST = 1; //拉高使能端,产生上升沿开始写数据
\tfor(i = 0; i < 8; i++)//每次写1位,写8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线
\t\tTIO = cmd & 0x01;//写1位数据,从最低位开始写
\t\tTSCLK = 1;\t\t //拉高时钟总线,产生上升沿数据被DS1302读走
\t\tcmd >>=1;\t\t //右移一位
\t}
\tfor(i = 0; i < 8; i++)//每次读1位,读8次
\t{
\t\tTSCLK = 0;\t\t //拉低时钟总线,产生下降沿,DS1302把数据放到TIO上
\t\tdat >>= 1;\t\t //右移一位
\t\tif(TIO)\tdat |= 0x80;//读取数据,从最低位开始
\t\tTSCLK = 1;\t\t\t//拉高时钟总线,以备下一次产生下降沿
\t}
\treturn dat;\t//返回读出数据
}

//数据转BCD码
uchar Dat_Chg_BCD(uchar dat)
{
\tuchar dat1, dat2;
\tdat1 = dat / 10;
\tdat2 = dat % 10;
\tdat2 = dat2 + dat1 * 16;
\treturn dat2;
}

//BCD码转换为数据
uchar BCD_Chg_Dat(uchar dat)
{
\tuchar dat1, dat2;
\tdat1 = dat / 16;
\tdat2 = dat % 16;
\tdat2 = dat2 + dat1 * 10;
\treturn dat2;
}

void main()
{
\tuchar i;
\tuchar Sec, Min, Hour;
\tWrite_DS1302_DAT(0x8e, 0);//清除写保护
\tWrite_DS1302_DAT(0x80, Dat_Chg_BCD(30));//30秒(并且进行BCD码转换)
\tWrite_DS1302_DAT(0x82, Dat_Chg_BCD(15));//15分
\tWrite_DS1302_DAT(0x84, Dat_Chg_BCD(19));//19时
\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护
\twhile(1)
\t{
\t\tWrite_DS1302_DAT(0x8e, 0); //清除写保护
\t\tSec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));//读秒寄存器(并且进行BCD码转换)
\t\tMin\t= BCD_Chg_Dat(Read_DS1302_DAT(0x83));//读分寄存器
\t\tHour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));//读时寄存器
\t\tWrite_DS1302_DAT(0x8e, 0x80);//开写保护
\t\tfor(i = 0; i < 50; i++)\t//循环显示时钟
\t\t\tDisplay(Hour, Min, Sec);

\t}
}
\n"},{"title":"Hexo添加百度统计分析","description":"Hexo添加百度统计分析","abbrlink":"c8eb4af9","date":"2021-08-29T02:20:29.000Z","swiper_index":6,"cover":"https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png","_content":"\n\n1. 注册登录\n登录百度统计的[官方网站](https://tongji.baidu.com/web/welcome/login)\n\n2. 添加网站,填写相关信息\n\n ![image-20210830205228402](https://gitee.com/ajream/images/raw/master/img/20210830205231_image-20210830205228402.png)\n\n3. 获取代码![image-20210830205615284](https://gitee.com/ajream/images/raw/master/img/20210830205616_image-20210830205615284.png)\n\n 将上面这串字符填在主题的配置文件`_config.butterfly.yml`中:\n\n ```yml\n # Baidu Analytics\n # https://tongji.baidu.com/web/welcome/login\n baidu_analytics: #填在这里\n \n ```\n\n \n\n","source":"_posts/Hexo/Hexo添加百度统计分析.md","raw":"---\ntitle: Hexo添加百度统计分析\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: Hexo添加百度统计分析\nabbrlink: c8eb4af9\ndate: 2021-08-29 10:20:29\nswiper_index: 6\ncover: https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png\n---\n\n\n1. 注册登录\n登录百度统计的[官方网站](https://tongji.baidu.com/web/welcome/login)\n\n2. 添加网站,填写相关信息\n\n ![image-20210830205228402](https://gitee.com/ajream/images/raw/master/img/20210830205231_image-20210830205228402.png)\n\n3. 获取代码![image-20210830205615284](https://gitee.com/ajream/images/raw/master/img/20210830205616_image-20210830205615284.png)\n\n 将上面这串字符填在主题的配置文件`_config.butterfly.yml`中:\n\n ```yml\n # Baidu Analytics\n # https://tongji.baidu.com/web/welcome/login\n baidu_analytics: #填在这里\n \n ```\n\n \n\n","slug":"Hexo/Hexo添加百度统计分析","published":1,"updated":"2021-09-16T15:31:14.302Z","_id":"cktk8o6ob000jakve6uur2o8z","comments":1,"layout":"post","photos":[],"link":"","content":"
    \n
  1. \n

    注册登录
    \n登录百度统计的官方网站

    \n
  2. \n
  3. \n

    添加网站,填写相关信息

    \n

    \"image-20210830205228402\"

    \n
  4. \n
  5. \n

    获取代码\"image-20210830205615284\"

    \n

    将上面这串字符填在主题的配置文件_config.butterfly.yml中:

    \n
    1
    2
    3
    4
    # Baidu Analytics
    # https://tongji.baidu.com/web/welcome/login
    baidu_analytics: #填在这里

    \n
  6. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"
    \n
  1. \n

    注册登录
    \n登录百度统计的官方网站

    \n
  2. \n
  3. \n

    添加网站,填写相关信息

    \n

    \"image-20210830205228402\"

    \n
  4. \n
  5. \n

    获取代码\"image-20210830205615284\"

    \n

    将上面这串字符填在主题的配置文件_config.butterfly.yml中:

    \n
    1
    2
    3
    4
    # Baidu Analytics
    # https://tongji.baidu.com/web/welcome/login
    baidu_analytics: #填在这里

    \n
  6. \n
\n"},{"title":"51单片机学习笔记(六)","description":"DS18B20温度传感器介绍","abbrlink":"5dd9c9e3","date":"2021-08-20T07:02:48.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n\n\n## DS18B20介绍\n\nDS18B20数字温度传感器是DALLAS公司生产的单总线器件,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。\n\n\n\n### 特点\n\n1. 通信采用1-Wire接口\n2. n每个DS18B20都有唯一的64位序列码储存在板载ROM中\n3. 无需外部元件\n4. 可从数据线供电,电源范围为3.0V ~ 5.5V。\n5. 可测量的温度范围在-55℃ ~ +125℃\n6. 在-10~+85℃范围内精确度为±0.5℃\n7. 温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃\n\n\n\n\n\n![image-20210824153229536](https://gitee.com/ajream/images/raw/master/img/20210824153232_image-20210824153229536.png)\n\n\n\n## 单总线\n\nDS18B20采用`1-wire Bus`传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。\n\n单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。\n\n所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。\n\n\n\n\n\n### 初始化时序\n\n\n\n初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。\n\n主机和DS18B20做任何通讯前都需要对其初始化。\n\n初始化期间:\n\n1. 总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位\n\n2. 释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号\n\n\n\n![image-20210824154325250](https://gitee.com/ajream/images/raw/master/img/20210824154326_image-20210824154325250.png)\n\n\n\n\n\n### 写时序\n\n写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。\n\n所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。\n\n\n\n初始化写时序:\n\n总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。\n\n当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。\n\n\n\n进行写时序:\n\n总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0\n\n\n\n![image-20210824160214829](https://gitee.com/ajream/images/raw/master/img/20210824160216_image-20210824160214829.png)\n\n\n\n\n\n\n\n\n\n### 读时序\n\n1. 先把总线拉低至少1us\n2. 在15us内选取1us进行读数据\n3. 在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的\n4. 然后总线被释放\n5. 总时间必须>=60us\n\n![image-20210824161252256](https://gitee.com/ajream/images/raw/master/img/20210824161255_image-20210824161252256.png)\n\n\n\n读1的详细时序:\n\nTINIT,TRC 和 TSAMPLE 之和必须小于 15us\n\n\n\n![image-20210824162105674](https://gitee.com/ajream/images/raw/master/img/20210824162107_image-20210824162105674.png)\n\n\n\n推荐的读1时序:\n\n系统时间可以用下面办法达到最大:\n\nTINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。\n\n\n\n![image-20210824162319244](https://gitee.com/ajream/images/raw/master/img/20210824162320_image-20210824162319244.png)\n\n\n\n\n\n\n\n## 数据获取\n\n\n\n通过单线总线端口访问DS18B20的协议如下:\n\n\n\n1. 初始化\n\n2. ROM操作指令\n\n3. DS18B20功能指令、温度转换命令、读取暂存器命令\n\n\n\n部分常用ROM指令如下:\n\n![image-20210824163430400](https://gitee.com/ajream/images/raw/master/img/20210824163431_image-20210824163430400.png)\n\n\n\n\n\n## 例子\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit DS = P2^2; //DS18B20单总线\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/\nvoid Delay_us(uchar us)\n{\n\twhile(us--);\t\n}\n/*====================================\n函数:void Display(INT16U Value)\n参数:Value,显示值 取值0-65535\n描述:共阴极数码管显示函数可显示一个字节的数\n====================================*/\nvoid Display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型\n{\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\tP0 = table[Value/100];\t\t//数码管显示百位\n\tDU = 1;\t\t\t\t\t\t\t//打开段选\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tP0 = T_COM[0];\t\t\t\t //第一位数码管\n\tWE = 1;\t\t\t\t\t\t//打开位选\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%100/10]|0x80; //显示十位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t //第二位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%10];\t\t//显示个位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[2];\t\t\t\t//第三位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n}\n/*单总线初始化时序*/\nbit ds_init()\n{\n\tbit i;\n\tDS = 1;\n\t_nop_();\n\tDS = 0;\n\tDelay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位\n\tDS = 1; //释放总线\n\tDelay_us(4); //延时37.95us 等待18B20发回存在信号\n\ti = DS;\n\tDelay_us(20); //141.95us\n\tDS = 1;\n\t_nop_();\n\treturn (i);\n}\n/*写一个字节*/\nvoid write_byte(uchar dat)\n{\n\tuchar i;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生些时序\n\t\tDS = dat & 0x01;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1; //释放总线准备下一次数据写入\n\t\t_nop_();\n\t\tdat >>= 1;\n\t}\n}\n\nuchar read_byte()\n{\n\tuchar i, j, dat;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生读时序\n\t\tDS = 1;\n\t\t_nop_();//释放总线\n\t\tj = DS;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1;\n\t\t_nop_();\n\t\tdat = (j<<7)|(dat>>1);\t\n\t}\n\treturn (dat);\n}\nvoid main()\n{\n\tuint i;\n\tuchar L, M;\n/*\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令\n\twrite_byte(0x4e);//写暂存器指令\n\twrite_byte(0x7f);\n\twrite_byte(0xf7);\n\twrite_byte(0x1f);//配置工作在9位模式下\n\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令 \n\twrite_byte(0x48);*/\n\twhile(1)\n\t{\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0x44);//发送温度转换指令\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0xbe);//读取DS18B20暂存器值\n\t\tL = read_byte();\n\t\tM = read_byte();\n\t\ti = M;\n\t\ti <<= 8;\n\t\ti |= L;\t\t\t\t\t\t\n\t\ti = i * 0.0625 * 10 + 0.5;\n\t\tDisplay(i);\n\t}\n}\n```\n\n","source":"_posts/51单片机/51单片机学习笔记(六).md","raw":"---\ntitle: 51单片机学习笔记(六)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: DS18B20温度传感器介绍\nabbrlink: 5dd9c9e3\ndate: 2021-08-20 15:02:48\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n\n\n## DS18B20介绍\n\nDS18B20数字温度传感器是DALLAS公司生产的单总线器件,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。\n\n\n\n### 特点\n\n1. 通信采用1-Wire接口\n2. n每个DS18B20都有唯一的64位序列码储存在板载ROM中\n3. 无需外部元件\n4. 可从数据线供电,电源范围为3.0V ~ 5.5V。\n5. 可测量的温度范围在-55℃ ~ +125℃\n6. 在-10~+85℃范围内精确度为±0.5℃\n7. 温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃\n\n\n\n\n\n![image-20210824153229536](https://gitee.com/ajream/images/raw/master/img/20210824153232_image-20210824153229536.png)\n\n\n\n## 单总线\n\nDS18B20采用`1-wire Bus`传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。\n\n单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。\n\n所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。\n\n\n\n\n\n### 初始化时序\n\n\n\n初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。\n\n主机和DS18B20做任何通讯前都需要对其初始化。\n\n初始化期间:\n\n1. 总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位\n\n2. 释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号\n\n\n\n![image-20210824154325250](https://gitee.com/ajream/images/raw/master/img/20210824154326_image-20210824154325250.png)\n\n\n\n\n\n### 写时序\n\n写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。\n\n所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。\n\n\n\n初始化写时序:\n\n总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。\n\n当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。\n\n\n\n进行写时序:\n\n总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0\n\n\n\n![image-20210824160214829](https://gitee.com/ajream/images/raw/master/img/20210824160216_image-20210824160214829.png)\n\n\n\n\n\n\n\n\n\n### 读时序\n\n1. 先把总线拉低至少1us\n2. 在15us内选取1us进行读数据\n3. 在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的\n4. 然后总线被释放\n5. 总时间必须>=60us\n\n![image-20210824161252256](https://gitee.com/ajream/images/raw/master/img/20210824161255_image-20210824161252256.png)\n\n\n\n读1的详细时序:\n\nTINIT,TRC 和 TSAMPLE 之和必须小于 15us\n\n\n\n![image-20210824162105674](https://gitee.com/ajream/images/raw/master/img/20210824162107_image-20210824162105674.png)\n\n\n\n推荐的读1时序:\n\n系统时间可以用下面办法达到最大:\n\nTINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。\n\n\n\n![image-20210824162319244](https://gitee.com/ajream/images/raw/master/img/20210824162320_image-20210824162319244.png)\n\n\n\n\n\n\n\n## 数据获取\n\n\n\n通过单线总线端口访问DS18B20的协议如下:\n\n\n\n1. 初始化\n\n2. ROM操作指令\n\n3. DS18B20功能指令、温度转换命令、读取暂存器命令\n\n\n\n部分常用ROM指令如下:\n\n![image-20210824163430400](https://gitee.com/ajream/images/raw/master/img/20210824163431_image-20210824163430400.png)\n\n\n\n\n\n## 例子\n\n```c\n#include \n#include \n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n 自定义类型名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\n\n/*====================================\n 硬件接口位声明\n====================================*/\nsbit DS = P2^2; //DS18B20单总线\nsbit DU = P2^6; //数码管段选\nsbit WE = P2^7; //数码管位选\n/*====================================\n共阴极数码管段选码\n====================================*/\nuchar code table[]={ \n//0\t\t1\t 2 3 4 5 6 7 8\n0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,\n//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示\n0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00\n };\n\n/*====================================\n数码管位选码\n====================================*/\n\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位\nuchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数:void Delay_Ms(INT16U ms)\n参数:ms,毫秒延时形参\n描述:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid Delay_Ms(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/\nvoid Delay_us(uchar us)\n{\n\twhile(us--);\t\n}\n/*====================================\n函数:void Display(INT16U Value)\n参数:Value,显示值 取值0-65535\n描述:共阴极数码管显示函数可显示一个字节的数\n====================================*/\nvoid Display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型\n{\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\tP0 = table[Value/100];\t\t//数码管显示百位\n\tDU = 1;\t\t\t\t\t\t\t//打开段选\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tP0 = T_COM[0];\t\t\t\t //第一位数码管\n\tWE = 1;\t\t\t\t\t\t//打开位选\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%100/10]|0x80; //显示十位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[1];\t\t\t //第二位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = table[Value%10];\t\t//显示个位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = T_COM[2];\t\t\t\t//第三位数码管\n\tWE = 1;\n\tWE = 0;\n\tDelay_Ms(3);\n}\n/*单总线初始化时序*/\nbit ds_init()\n{\n\tbit i;\n\tDS = 1;\n\t_nop_();\n\tDS = 0;\n\tDelay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位\n\tDS = 1; //释放总线\n\tDelay_us(4); //延时37.95us 等待18B20发回存在信号\n\ti = DS;\n\tDelay_us(20); //141.95us\n\tDS = 1;\n\t_nop_();\n\treturn (i);\n}\n/*写一个字节*/\nvoid write_byte(uchar dat)\n{\n\tuchar i;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生些时序\n\t\tDS = dat & 0x01;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1; //释放总线准备下一次数据写入\n\t\t_nop_();\n\t\tdat >>= 1;\n\t}\n}\n\nuchar read_byte()\n{\n\tuchar i, j, dat;\n\tfor(i=0;i<8;i++)\n\t{\n\t\tDS = 0;\n\t\t_nop_();//产生读时序\n\t\tDS = 1;\n\t\t_nop_();//释放总线\n\t\tj = DS;\n\t\tDelay_us(10);//76.95us\n\t\tDS = 1;\n\t\t_nop_();\n\t\tdat = (j<<7)|(dat>>1);\t\n\t}\n\treturn (dat);\n}\nvoid main()\n{\n\tuint i;\n\tuchar L, M;\n/*\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令\n\twrite_byte(0x4e);//写暂存器指令\n\twrite_byte(0x7f);\n\twrite_byte(0xf7);\n\twrite_byte(0x1f);//配置工作在9位模式下\n\tds_init();//初始化DS18B20\n\twrite_byte(0xcc);//发送跳跃ROM指令 \n\twrite_byte(0x48);*/\n\twhile(1)\n\t{\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0x44);//发送温度转换指令\n\t\tds_init();//初始化DS18B20\n\t\twrite_byte(0xcc);//发送跳跃ROM指令\n\t\twrite_byte(0xbe);//读取DS18B20暂存器值\n\t\tL = read_byte();\n\t\tM = read_byte();\n\t\ti = M;\n\t\ti <<= 8;\n\t\ti |= L;\t\t\t\t\t\t\n\t\ti = i * 0.0625 * 10 + 0.5;\n\t\tDisplay(i);\n\t}\n}\n```\n\n","slug":"51单片机/51单片机学习笔记(六)","published":1,"updated":"2021-09-10T12:45:19.484Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6od000oakveeb9m24v2","content":"

DS18B20介绍

DS18B20数字温度传感器是DALLAS公司生产的单总线器件,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。

\n

特点

    \n
  1. 通信采用1-Wire接口
  2. \n
  3. n每个DS18B20都有唯一的64位序列码储存在板载ROM中
  4. \n
  5. 无需外部元件
  6. \n
  7. 可从数据线供电,电源范围为3.0V ~ 5.5V。
  8. \n
  9. 可测量的温度范围在-55℃ ~ +125℃
  10. \n
  11. 在-10~+85℃范围内精确度为±0.5℃
  12. \n
  13. 温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃
  14. \n
\n

\"image-20210824153229536\"

\n

单总线

DS18B20采用1-wire Bus传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。

\n

单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。

\n

所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。

\n

初始化时序

初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。

\n

主机和DS18B20做任何通讯前都需要对其初始化。

\n

初始化期间:

\n
    \n
  1. 总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位

    \n
  2. \n
  3. 释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号

    \n
  4. \n
\n

\"image-20210824154325250\"

\n

写时序

写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。

\n

所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。

\n

初始化写时序:

\n

总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。

\n

当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。

\n

进行写时序:

\n

总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0

\n

\"image-20210824160214829\"

\n

读时序

    \n
  1. 先把总线拉低至少1us
  2. \n
  3. 在15us内选取1us进行读数据
  4. \n
  5. 在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的
  6. \n
  7. 然后总线被释放
  8. \n
  9. 总时间必须>=60us
  10. \n
\n

\"image-20210824161252256\"

\n

读1的详细时序:

\n

TINIT,TRC 和 TSAMPLE 之和必须小于 15us

\n

\"image-20210824162105674\"

\n

推荐的读1时序:

\n

系统时间可以用下面办法达到最大:

\n

TINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。

\n

\"image-20210824162319244\"

\n

数据获取

通过单线总线端口访问DS18B20的协议如下:

\n
    \n
  1. 初始化

    \n
  2. \n
  3. ROM操作指令

    \n
  4. \n
  5. DS18B20功能指令、温度转换命令、读取暂存器命令

    \n
  6. \n
\n

部分常用ROM指令如下:

\n

\"image-20210824163430400\"

\n

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <reg52.h>
#include <intrins.h>
#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit DS = P2^2; //DS18B20单总线
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
/*====================================
共阴极数码管段选码
====================================*/
uchar code table[]={
//0\t\t1\t 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
};

/*====================================
数码管位选码
====================================*/
\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数:void Delay_Ms(INT16U ms)
参数:ms,毫秒延时形参
描述:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void Delay_Ms(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}
/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/
void Delay_us(uchar us)
{
\twhile(us--);\t
}
/*====================================
函数:void Display(INT16U Value)
参数:Value,显示值 取值0-65535
描述:共阴极数码管显示函数可显示一个字节的数
====================================*/
void Display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型
{\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t//关闭段选
\tP0 = table[Value/100];\t\t//数码管显示百位
\tDU = 1;\t\t\t\t\t\t\t//打开段选
\tDU = 0;\t\t\t\t\t\t\t//关闭段选

\tWE = 0;\t\t\t\t\t\t//关闭位选
\tP0 = T_COM[0];\t\t\t\t //第一位数码管
\tWE = 1;\t\t\t\t\t\t//打开位选
\tWE = 0;\t\t\t\t\t\t//关闭位选
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Value%100/10]|0x80; //显示十位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[1];\t\t\t //第二位数码管
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Value%10];\t\t//显示个位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[2];\t\t\t\t//第三位数码管
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
}
/*单总线初始化时序*/
bit ds_init()
{
\tbit i;
\tDS = 1;
\t_nop_();
\tDS = 0;
\tDelay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位
\tDS = 1; //释放总线
\tDelay_us(4); //延时37.95us 等待18B20发回存在信号
\ti = DS;
\tDelay_us(20); //141.95us
\tDS = 1;
\t_nop_();
\treturn (i);
}
/*写一个字节*/
void write_byte(uchar dat)
{
\tuchar i;
\tfor(i=0;i<8;i++)
\t{
\t\tDS = 0;
\t\t_nop_();//产生些时序
\t\tDS = dat & 0x01;
\t\tDelay_us(10);//76.95us
\t\tDS = 1; //释放总线准备下一次数据写入
\t\t_nop_();
\t\tdat >>= 1;
\t}
}

uchar read_byte()
{
\tuchar i, j, dat;
\tfor(i=0;i<8;i++)
\t{
\t\tDS = 0;
\t\t_nop_();//产生读时序
\t\tDS = 1;
\t\t_nop_();//释放总线
\t\tj = DS;
\t\tDelay_us(10);//76.95us
\t\tDS = 1;
\t\t_nop_();
\t\tdat = (j<<7)|(dat>>1);\t
\t}
\treturn (dat);
}
void main()
{
\tuint i;
\tuchar L, M;
/*\tds_init();//初始化DS18B20
\twrite_byte(0xcc);//发送跳跃ROM指令
\twrite_byte(0x4e);//写暂存器指令
\twrite_byte(0x7f);
\twrite_byte(0xf7);
\twrite_byte(0x1f);//配置工作在9位模式下
\tds_init();//初始化DS18B20
\twrite_byte(0xcc);//发送跳跃ROM指令
\twrite_byte(0x48);*/
\twhile(1)
\t{
\t\tds_init();//初始化DS18B20
\t\twrite_byte(0xcc);//发送跳跃ROM指令
\t\twrite_byte(0x44);//发送温度转换指令
\t\tds_init();//初始化DS18B20
\t\twrite_byte(0xcc);//发送跳跃ROM指令
\t\twrite_byte(0xbe);//读取DS18B20暂存器值
\t\tL = read_byte();
\t\tM = read_byte();
\t\ti = M;
\t\ti <<= 8;
\t\ti |= L;\t\t\t\t\t\t
\t\ti = i * 0.0625 * 10 + 0.5;
\t\tDisplay(i);
\t}
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

DS18B20介绍

DS18B20数字温度传感器是DALLAS公司生产的单总线器件,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。

\n

特点

    \n
  1. 通信采用1-Wire接口
  2. \n
  3. n每个DS18B20都有唯一的64位序列码储存在板载ROM中
  4. \n
  5. 无需外部元件
  6. \n
  7. 可从数据线供电,电源范围为3.0V ~ 5.5V。
  8. \n
  9. 可测量的温度范围在-55℃ ~ +125℃
  10. \n
  11. 在-10~+85℃范围内精确度为±0.5℃
  12. \n
  13. 温度计分辨率可设置为9~12位,12位时分辨率对应为0.0625℃
  14. \n
\n

\"image-20210824153229536\"

\n

单总线

DS18B20采用1-wire Bus传输数据,所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。

\n

单总线信号类型: 复位脉冲、存在脉冲、写0、写1、读0、读1。

\n

所有这些信号除存在脉冲由DS18B20发出的以外,其他信号都由总线控制器发出。并且,数据传输总是从最低有效位开始。

\n

初始化时序

初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。

\n

主机和DS18B20做任何通讯前都需要对其初始化。

\n

初始化期间:

\n
    \n
  1. 总线控制器拉低总线并保持480us以上,挂在总线上的器件将被复位

    \n
  2. \n
  3. 释放总线,等到15-60us,此时18B20将返回一个60~240us之间的低电平存在信号

    \n
  4. \n
\n

\"image-20210824154325250\"

\n

写时序

写时序有2种写法:写0时序和写1时序。总线控制器通过写 1 时序写逻辑 1 到 DS18B20,写 0 时序写逻辑 0 到 DS18B20。

\n

所有写时序必须持续 >=60us,包括:两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。

\n

初始化写时序:

\n

总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。

\n

当总线被释放的时候,5K 的上拉电阻将拉高总线。因此,如果总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持(至少 60us)。

\n

进行写时序:

\n

总线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采 样。如果线上是高电平,就是写1。如果线上是低电平,就是写0

\n

\"image-20210824160214829\"

\n

读时序

    \n
  1. 先把总线拉低至少1us
  2. \n
  3. 在15us内选取1us进行读数据
  4. \n
  5. 在拉低的15us(包括上面2点所说的1us)内传输的数据是有效的
  6. \n
  7. 然后总线被释放
  8. \n
  9. 总时间必须>=60us
  10. \n
\n

\"image-20210824161252256\"

\n

读1的详细时序:

\n

TINIT,TRC 和 TSAMPLE 之和必须小于 15us

\n

\"image-20210824162105674\"

\n

推荐的读1时序:

\n

系统时间可以用下面办法达到最大:

\n

TINIT 和 TRC 保持时间尽可能小;把控制器采样时间放 到 15us 周期的最后。

\n

\"image-20210824162319244\"

\n

数据获取

通过单线总线端口访问DS18B20的协议如下:

\n
    \n
  1. 初始化

    \n
  2. \n
  3. ROM操作指令

    \n
  4. \n
  5. DS18B20功能指令、温度转换命令、读取暂存器命令

    \n
  6. \n
\n

部分常用ROM指令如下:

\n

\"image-20210824163430400\"

\n

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <reg52.h>
#include <intrins.h>
#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;

typedef unsigned int INT16U;
typedef unsigned int uint;

/*====================================
硬件接口位声明
====================================*/
sbit DS = P2^2; //DS18B20单总线
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
/*====================================
共阴极数码管段选码
====================================*/
uchar code table[]={
//0\t\t1\t 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B\t C\t D\t E\t F\t\t-\t .\t 关显示
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
};

/*====================================
数码管位选码
====================================*/
\t\t\t\t //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数:void Delay_Ms(INT16U ms)
参数:ms,毫秒延时形参
描述:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void Delay_Ms(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}
/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/
void Delay_us(uchar us)
{
\twhile(us--);\t
}
/*====================================
函数:void Display(INT16U Value)
参数:Value,显示值 取值0-65535
描述:共阴极数码管显示函数可显示一个字节的数
====================================*/
void Display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型
{\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t//关闭段选
\tP0 = table[Value/100];\t\t//数码管显示百位
\tDU = 1;\t\t\t\t\t\t\t//打开段选
\tDU = 0;\t\t\t\t\t\t\t//关闭段选

\tWE = 0;\t\t\t\t\t\t//关闭位选
\tP0 = T_COM[0];\t\t\t\t //第一位数码管
\tWE = 1;\t\t\t\t\t\t//打开位选
\tWE = 0;\t\t\t\t\t\t//关闭位选
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Value%100/10]|0x80; //显示十位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[1];\t\t\t //第二位数码管
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
//-------------------------------
\tDU = 0;
\tP0 = table[Value%10];\t\t//显示个位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = T_COM[2];\t\t\t\t//第三位数码管
\tWE = 1;
\tWE = 0;
\tDelay_Ms(3);
}
/*单总线初始化时序*/
bit ds_init()
{
\tbit i;
\tDS = 1;
\t_nop_();
\tDS = 0;
\tDelay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位
\tDS = 1; //释放总线
\tDelay_us(4); //延时37.95us 等待18B20发回存在信号
\ti = DS;
\tDelay_us(20); //141.95us
\tDS = 1;
\t_nop_();
\treturn (i);
}
/*写一个字节*/
void write_byte(uchar dat)
{
\tuchar i;
\tfor(i=0;i<8;i++)
\t{
\t\tDS = 0;
\t\t_nop_();//产生些时序
\t\tDS = dat & 0x01;
\t\tDelay_us(10);//76.95us
\t\tDS = 1; //释放总线准备下一次数据写入
\t\t_nop_();
\t\tdat >>= 1;
\t}
}

uchar read_byte()
{
\tuchar i, j, dat;
\tfor(i=0;i<8;i++)
\t{
\t\tDS = 0;
\t\t_nop_();//产生读时序
\t\tDS = 1;
\t\t_nop_();//释放总线
\t\tj = DS;
\t\tDelay_us(10);//76.95us
\t\tDS = 1;
\t\t_nop_();
\t\tdat = (j<<7)|(dat>>1);\t
\t}
\treturn (dat);
}
void main()
{
\tuint i;
\tuchar L, M;
/*\tds_init();//初始化DS18B20
\twrite_byte(0xcc);//发送跳跃ROM指令
\twrite_byte(0x4e);//写暂存器指令
\twrite_byte(0x7f);
\twrite_byte(0xf7);
\twrite_byte(0x1f);//配置工作在9位模式下
\tds_init();//初始化DS18B20
\twrite_byte(0xcc);//发送跳跃ROM指令
\twrite_byte(0x48);*/
\twhile(1)
\t{
\t\tds_init();//初始化DS18B20
\t\twrite_byte(0xcc);//发送跳跃ROM指令
\t\twrite_byte(0x44);//发送温度转换指令
\t\tds_init();//初始化DS18B20
\t\twrite_byte(0xcc);//发送跳跃ROM指令
\t\twrite_byte(0xbe);//读取DS18B20暂存器值
\t\tL = read_byte();
\t\tM = read_byte();
\t\ti = M;
\t\ti <<= 8;
\t\ti |= L;\t\t\t\t\t\t
\t\ti = i * 0.0625 * 10 + 0.5;
\t\tDisplay(i);
\t}
}
\n"},{"title":"Github Actions自动部署","date":"2021-09-13T12:13:46.000Z","description":"使用Github的Actions功能自动部署Hexo博客","cover":"https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png","abbrlink":"88ad757","_content":"\n\n\n## 写在前面\n\n\n\n\n\n没用过Actions的建议细看,好好理解其工作原理和流程\n\n本文大部分参考 [糖果屋] 的教程,详见[糖果屋-使用Github Action实现全自动部署](https://akilar.top/posts/f752c86d/), 感谢大佬!\n\n## Github Actions简介\n\nGithub Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句\n\n在这里可以让脚本自动执行 `hexo cl && hexo g -d` 三连操作\n\n\n\n\n\n\n\n## 获取令牌(Token)\n\n为了确保交由 `Github Action` 来持续部署时,`Github Action` 具备足够的权限来进行 `hexo deploy` 操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 `Token`,博主分别在 `Github`、`Gitee`、`Coding` 处部署了静态页面,所以也就需要获取这三处的 `Token`。\n\n当我们获取了token后,在 `_config.yml`文件的 `deploy` 项就可以使用了\n\n{%tabs test,-1%}\n\n\n\n访问 [Github-> 头像(右上角)->Settings->Developer Settings->Personal access tokens](https://github.com/settings/tokens)->generate new token, 创建的 `Token` 名称随意,但必须勾选 repo 项。\n\n![image-20210913220005116](https://gitee.com/ajream/images/raw/master/img/20210913220008_image-20210913220005116.png)\n\n![image-20210913220422469](https://gitee.com/ajream/images/raw/master/img/20210913220423_image-20210913220422469.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n访问 [Gitee-> 头像(右上角)-> 设置 -> 私人令牌](https://gitee.com/profile/personal_access_tokens) -> 生成新令牌\n\n![image-20210913221257282](https://gitee.com/ajream/images/raw/master/img/20210913221258_image-20210913221257282.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n\n\n访问 [Coding](https://coding.net/)-> 头像(右上角)-> 个人账户设置 -> 访问令牌 -> 新建令牌。\n\n![image-20210913221822733](https://gitee.com/ajream/images/raw/master/img/20210913221824_image-20210913221822733.png)\n\n\n\n\n\n![image-20210913221754924](https://gitee.com/ajream/images/raw/master/img/20210913221756_image-20210913221754924.png)\n\n\n\n{% note warning modern %}\n\n注意coding平台与Github、Gitee不同,它生成的令牌还有【令牌用户名】,并且只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`和`令牌用户名`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n{%endtabs%}\n\n\n\n\n\n\n\n## 创建存放源码的私有仓库\n\n{%folding blue, 展开%}\n\n我们需要创建一个用来存放 `Hexo` 博客源码的私有仓库 `[SourceRepo]`,这点在 [Win10](https://akilar.top/posts/6ef63e2d/) 的 `Hexo` 博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)\n\n\n创建完成后,需要把博客的源码 `push` 到这里。首先获取远程仓库地址,此处虽然 `SSH` 和 `HTTPS` 均可。`SSH` 在绑定过 `ssh key` 的设备上无需再输入密码,`HTTPS` 则需要输入密码,但是 `SSH` 偶尔会遇到端口占用的情况。请自主选择。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)\n\n\n\n{%note default modern%}\n\n这里之所以是私有仓库,是因为在接下来的配置中会用到 `Token`,如果 `Token` 被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n## 配置 deploy 项\n\n\n\n打开站点配置文件 `[Blogroot]/_config.yml`, 找到 `deploy` 配置项,使用之前生成的 `[SiteToken]` 和各个站点仓库 `URL` 来组装地址。\n\n\n\n```yml\ndeploy:\n- type: git\n repo:\n gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch]\n gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch]\n coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]\n # [,branch]为可选项,表示部署的分支\n #2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n{%note default modern%}\n\n注意:Gitee的用户名如果含有大写字母,要改为小写; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次\n\n{%endnote%}\n\n\n\n参考我个人的写法:\n\n\n\n```yml\ndeploy:\n type: git\n repo: \n github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master\n gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master\n coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master\n```\n\n\n\n\n\n## 配置 Github Action\n\n\n\n在 `[Blogroot]` 新建`.github` 文件夹,注意开头是有个`.` 的。然后在`.github` 内新建 `workflows` 文件夹,再在 `workflows` 文件夹内新建 `autodeploy.yml`, 在 `[Blogroot]/.github/workflows/autodeploy.yml` 里面输入\n\n```yml\n# 当有改动推送到master分支时,启动Action\nname: 自动部署\n\non:\n push:\n branches:\n - master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n release:\n types:\n - published\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - name: 检查分支\n uses: actions/checkout@v2\n with:\n ref: master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n - name: 安装 Node\n uses: actions/setup-node@v1\n with:\n node-version: \"12.x\"\n\n - name: 安装 Hexo\n run: |\n export TZ='Asia/Shanghai'\n npm install hexo-cli -g\n\n - name: 缓存 Hexo\n uses: actions/cache@v1\n id: cache\n with:\n path: node_modules\n key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}\n\n - name: 安装依赖\n if: steps.cache.outputs.cache-hit != 'true'\n run: |\n npm install --save\n\n - name: 生成静态文件\n run: |\n hexo clean\n hexo generate\n\n - name: 部署\n run: |\n git config --global user.name \"[GithubUsername]\"\n git config --global user.email \"[GithubEmail]\"\n git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git\n # 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患\n # =====注意.deploy_git前面有个空格=====\n # 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件\n hexo deploy\n```\n\n\n\n{%note info modern%}\n\n注意看代码里面的注释\n\n{%endnote%}\n\n\n\n\n\n## 重新设置远程仓库和分支\n\n\n\n{%tabs gggg, -1%}\n\n\n\n1. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git\n ```\n\n 如果不是 `butterfly` 主题,记得替换最后一行内容为你自己当前使用的主题\n\n2. 提交源码到刚才新建的私有仓库 \n\n 在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。\n\n ```sh\n git remote rm origin # 删除原有仓库链接\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为新的存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n git add .\n git commit -m \"github action update\"\n git push origin master\n \n #2020年10月后github新建仓库默认分支改为main,注意更改\n ```\n\n\n\n> 可能遇到的 bug\n> 因为 `butterfly` 主题文件夹下的`.git` 文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 `themes` 文件夹移动到别的目录下。然后 `commit` 一次。接着将 `themes` 文件夹移动回来,再 `commit` 一次。\n\n\n\n\n\n\n\n\n\n1. 删除或者先把 `[Blogroot]/themes/butterfly/.git` 移动到非博客文件夹目录下,原因是主题文件夹下的`.git` 文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。\n\n2. 在博客根目录 `[Blogroot]` 路径下运行指令\n\n ```sh\n git init #初始化\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n ```\n\n3. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git # 如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题\n ```\n\n\n\n完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署\n\n```sh\ngit add .\ngit commit -m \"github action update\"\ngit push origin master\n#2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的`.git` 放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)\n\n\n\n{%endtabs%}\n\n\n\n\n\n## 查看部署情况\n\n![image-20210913232006065](https://gitee.com/ajream/images/raw/master/img/20210913232007_image-20210913232006065.png)\n\n\n\n\n\n点击正在运行的workflow就可以查看部署情况\n\n![image-20210913233828678](https://gitee.com/ajream/images/raw/master/img/20210913233832_image-20210913233828678.png)\n\n\n\n![image-20210913234018254](https://gitee.com/ajream/images/raw/master/img/20210913234019_image-20210913234018254.png)\n\n## 我遇到的Bug\n\n\n\nBug:\n\n**Spawn Failed**\n\n原因主要就是配置deploy项时仓库的git链接没有正确配置,比如 :\n\n1. gitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的)\n2. 配置coding时没有仔细看教程,用户名必须要使用生成 `token` 时系统同时生成的令牌用户名\n\n\n\n\n\n**看板娘不能显示**\n\n删除 `BlogRoot/themes/butterfly/source/live2d-widget` 下的 `.git` 文件\n\n然后执行:\n\n```\ngit rm --cached /themes/butterfly/source/live2d-widget\ngit add .\ngit commit -m \"修复看板娘不能正常显示\"\ngit push origin master\n```\n\n \n\n","source":"_posts/Hexo/github Actions自动部署.md","raw":"---\ntitle: Github Actions自动部署\ndate: '2021-09-13 20:13:46'\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: 使用Github的Actions功能自动部署Hexo博客\ncover: https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png\nabbrlink: 88ad757\n---\n\n\n\n## 写在前面\n\n\n\n\n\n没用过Actions的建议细看,好好理解其工作原理和流程\n\n本文大部分参考 [糖果屋] 的教程,详见[糖果屋-使用Github Action实现全自动部署](https://akilar.top/posts/f752c86d/), 感谢大佬!\n\n## Github Actions简介\n\nGithub Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句\n\n在这里可以让脚本自动执行 `hexo cl && hexo g -d` 三连操作\n\n\n\n\n\n\n\n## 获取令牌(Token)\n\n为了确保交由 `Github Action` 来持续部署时,`Github Action` 具备足够的权限来进行 `hexo deploy` 操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 `Token`,博主分别在 `Github`、`Gitee`、`Coding` 处部署了静态页面,所以也就需要获取这三处的 `Token`。\n\n当我们获取了token后,在 `_config.yml`文件的 `deploy` 项就可以使用了\n\n{%tabs test,-1%}\n\n\n\n访问 [Github-> 头像(右上角)->Settings->Developer Settings->Personal access tokens](https://github.com/settings/tokens)->generate new token, 创建的 `Token` 名称随意,但必须勾选 repo 项。\n\n![image-20210913220005116](https://gitee.com/ajream/images/raw/master/img/20210913220008_image-20210913220005116.png)\n\n![image-20210913220422469](https://gitee.com/ajream/images/raw/master/img/20210913220423_image-20210913220422469.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n访问 [Gitee-> 头像(右上角)-> 设置 -> 私人令牌](https://gitee.com/profile/personal_access_tokens) -> 生成新令牌\n\n![image-20210913221257282](https://gitee.com/ajream/images/raw/master/img/20210913221258_image-20210913221257282.png)\n\n\n\n{% note warning modern %}\n\n`token` 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n\n\n访问 [Coding](https://coding.net/)-> 头像(右上角)-> 个人账户设置 -> 访问令牌 -> 新建令牌。\n\n![image-20210913221822733](https://gitee.com/ajream/images/raw/master/img/20210913221824_image-20210913221822733.png)\n\n\n\n\n\n![image-20210913221754924](https://gitee.com/ajream/images/raw/master/img/20210913221756_image-20210913221754924.png)\n\n\n\n{% note warning modern %}\n\n注意coding平台与Github、Gitee不同,它生成的令牌还有【令牌用户名】,并且只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 `Token`和`令牌用户名`。之后如果忘记了就只能重新生成重新配置了。\n\n{%endnote%}\n\n\n\n\n\n\n\n{%endtabs%}\n\n\n\n\n\n\n\n## 创建存放源码的私有仓库\n\n{%folding blue, 展开%}\n\n我们需要创建一个用来存放 `Hexo` 博客源码的私有仓库 `[SourceRepo]`,这点在 [Win10](https://akilar.top/posts/6ef63e2d/) 的 `Hexo` 博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/PZ7wmiuX8ae9rVK.png)\n\n\n创建完成后,需要把博客的源码 `push` 到这里。首先获取远程仓库地址,此处虽然 `SSH` 和 `HTTPS` 均可。`SSH` 在绑定过 `ssh key` 的设备上无需再输入密码,`HTTPS` 则需要输入密码,但是 `SSH` 偶尔会遇到端口占用的情况。请自主选择。\n[![img](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)](https://cdn.jsdelivr.net/npm/akilar-candyassets/image/mpdUYJKcfWLQ2Eg.png)\n\n\n\n{%note default modern%}\n\n这里之所以是私有仓库,是因为在接下来的配置中会用到 `Token`,如果 `Token` 被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n## 配置 deploy 项\n\n\n\n打开站点配置文件 `[Blogroot]/_config.yml`, 找到 `deploy` 配置项,使用之前生成的 `[SiteToken]` 和各个站点仓库 `URL` 来组装地址。\n\n\n\n```yml\ndeploy:\n- type: git\n repo:\n gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch]\n gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch]\n coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]\n # [,branch]为可选项,表示部署的分支\n #2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n{%note default modern%}\n\n注意:Gitee的用户名如果含有大写字母,要改为小写; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次\n\n{%endnote%}\n\n\n\n参考我个人的写法:\n\n\n\n```yml\ndeploy:\n type: git\n repo: \n github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master\n gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master\n coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master\n```\n\n\n\n\n\n## 配置 Github Action\n\n\n\n在 `[Blogroot]` 新建`.github` 文件夹,注意开头是有个`.` 的。然后在`.github` 内新建 `workflows` 文件夹,再在 `workflows` 文件夹内新建 `autodeploy.yml`, 在 `[Blogroot]/.github/workflows/autodeploy.yml` 里面输入\n\n```yml\n# 当有改动推送到master分支时,启动Action\nname: 自动部署\n\non:\n push:\n branches:\n - master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n release:\n types:\n - published\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - name: 检查分支\n uses: actions/checkout@v2\n with:\n ref: master #2020年10月后github新建仓库默认分支改为main,注意更改\n\n - name: 安装 Node\n uses: actions/setup-node@v1\n with:\n node-version: \"12.x\"\n\n - name: 安装 Hexo\n run: |\n export TZ='Asia/Shanghai'\n npm install hexo-cli -g\n\n - name: 缓存 Hexo\n uses: actions/cache@v1\n id: cache\n with:\n path: node_modules\n key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}\n\n - name: 安装依赖\n if: steps.cache.outputs.cache-hit != 'true'\n run: |\n npm install --save\n\n - name: 生成静态文件\n run: |\n hexo clean\n hexo generate\n\n - name: 部署\n run: |\n git config --global user.name \"[GithubUsername]\"\n git config --global user.email \"[GithubEmail]\"\n git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git\n # 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患\n # =====注意.deploy_git前面有个空格=====\n # 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件\n hexo deploy\n```\n\n\n\n{%note info modern%}\n\n注意看代码里面的注释\n\n{%endnote%}\n\n\n\n\n\n## 重新设置远程仓库和分支\n\n\n\n{%tabs gggg, -1%}\n\n\n\n1. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git\n ```\n\n 如果不是 `butterfly` 主题,记得替换最后一行内容为你自己当前使用的主题\n\n2. 提交源码到刚才新建的私有仓库 \n\n 在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。\n\n ```sh\n git remote rm origin # 删除原有仓库链接\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为新的存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n git add .\n git commit -m \"github action update\"\n git push origin master\n \n #2020年10月后github新建仓库默认分支改为main,注意更改\n ```\n\n\n\n> 可能遇到的 bug\n> 因为 `butterfly` 主题文件夹下的`.git` 文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 `themes` 文件夹移动到别的目录下。然后 `commit` 一次。接着将 `themes` 文件夹移动回来,再 `commit` 一次。\n\n\n\n\n\n\n\n\n\n1. 删除或者先把 `[Blogroot]/themes/butterfly/.git` 移动到非博客文件夹目录下,原因是主题文件夹下的`.git` 文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。\n\n2. 在博客根目录 `[Blogroot]` 路径下运行指令\n\n ```sh\n git init #初始化\n git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为存放源码的github私有仓库\n git checkout -b master # 切换到master分支,\n #2020年10月后github新建仓库默认分支改为main,注意更改\n # 如果不是,后面的所有设置的分支记得保持一致\n ```\n\n3. 添加屏蔽项\n 因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。\n 打开 `[Blogroot]/.gitignore`, 输入以下内容:\n\n ```sh\n .DS_Store\n Thumbs.db\n db.json\n *.log\n node_modules/\n public/\n .deploy*/\n .deploy_git*/\n .idea\n themes/butterfly/.git # 如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题\n ```\n\n\n\n完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署\n\n```sh\ngit add .\ngit commit -m \"github action update\"\ngit push origin master\n#2020年10月后github新建仓库默认分支改为main,注意更改\n```\n\n\n\n此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的`.git` 放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)\n\n\n\n{%endtabs%}\n\n\n\n\n\n## 查看部署情况\n\n![image-20210913232006065](https://gitee.com/ajream/images/raw/master/img/20210913232007_image-20210913232006065.png)\n\n\n\n\n\n点击正在运行的workflow就可以查看部署情况\n\n![image-20210913233828678](https://gitee.com/ajream/images/raw/master/img/20210913233832_image-20210913233828678.png)\n\n\n\n![image-20210913234018254](https://gitee.com/ajream/images/raw/master/img/20210913234019_image-20210913234018254.png)\n\n## 我遇到的Bug\n\n\n\nBug:\n\n**Spawn Failed**\n\n原因主要就是配置deploy项时仓库的git链接没有正确配置,比如 :\n\n1. gitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的)\n2. 配置coding时没有仔细看教程,用户名必须要使用生成 `token` 时系统同时生成的令牌用户名\n\n\n\n\n\n**看板娘不能显示**\n\n删除 `BlogRoot/themes/butterfly/source/live2d-widget` 下的 `.git` 文件\n\n然后执行:\n\n```\ngit rm --cached /themes/butterfly/source/live2d-widget\ngit add .\ngit commit -m \"修复看板娘不能正常显示\"\ngit push origin master\n```\n\n \n\n","slug":"Hexo/github Actions自动部署","published":1,"updated":"2021-09-16T15:29:49.622Z","_id":"cktk8o6oe000rakve4wdsbjyw","comments":1,"layout":"post","photos":[],"link":"","content":"

写在前面

\n

没用过Actions的建议细看,好好理解其工作原理和流程

\n

本文大部分参考 [糖果屋] 的教程,详见糖果屋-使用Github Action实现全自动部署, 感谢大佬!

\n

Github Actions简介

\n

Github Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句

\n

在这里可以让脚本自动执行 hexo cl && hexo g -d 三连操作

\n

获取令牌(Token)

\n

为了确保交由 Github Action 来持续部署时,Github Action 具备足够的权限来进行 hexo deploy 操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 Token,博主分别在 GithubGiteeCoding 处部署了静态页面,所以也就需要获取这三处的 Token

\n

当我们获取了token后,在 _config.yml文件的 deploy 项就可以使用了

\n

访问 Github-> 头像(右上角)->Settings->Developer Settings->Personal access tokens->generate new token, 创建的 Token 名称随意,但必须勾选 repo 项。

\n

\"image-20210913220005116\"

\n

\"image-20210913220422469\"

\n

token 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token。之后如果忘记了就只能重新生成重新配置了。

\n

访问 Gitee-> 头像(右上角)-> 设置 -> 私人令牌 -> 生成新令牌

\n

\"image-20210913221257282\"

\n

token 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token。之后如果忘记了就只能重新生成重新配置了。

\n

访问 Coding-> 头像(右上角)-> 个人账户设置 -> 访问令牌 -> 新建令牌。

\n

\"image-20210913221822733\"

\n

\"image-20210913221754924\"

\n

注意coding平台与Github、Gitee不同,它生成的令牌还有【令牌用户名】,并且只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token令牌用户名。之后如果忘记了就只能重新生成重新配置了。

\n
\n

创建存放源码的私有仓库

\n
展开 \n
\n

我们需要创建一个用来存放 Hexo 博客源码的私有仓库 [SourceRepo],这点在 Win10Hexo 博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。
\"img\"

创建完成后,需要把博客的源码 push 到这里。首先获取远程仓库地址,此处虽然 SSHHTTPS 均可。SSH 在绑定过 ssh key 的设备上无需再输入密码,HTTPS 则需要输入密码,但是 SSH 偶尔会遇到端口占用的情况。请自主选择。
\"img\"

这里之所以是私有仓库,是因为在接下来的配置中会用到 Token,如果 Token 被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。

\n
\n
\n

配置 deploy 项

\n

打开站点配置文件 [Blogroot]/_config.yml, 找到 deploy 配置项,使用之前生成的 [SiteToken] 和各个站点仓库 URL 来组装地址。

\n
1
2
3
4
5
6
7
8
deploy:
- type: git
repo:
gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch]
gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch]
coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]
# [,branch]为可选项,表示部署的分支
#2020年10月后github新建仓库默认分支改为main,注意更改
\n

注意:Gitee的用户名如果含有大写字母,要改为小写; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次

\n
\n

参考我个人的写法:

\n
1
2
3
4
5
6
deploy:
type: git
repo:
github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master
gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master
coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master
\n

配置 Github Action

\n

[Blogroot] 新建.github 文件夹,注意开头是有个. 的。然后在.github 内新建 workflows 文件夹,再在 workflows 文件夹内新建 autodeploy.yml, 在 [Blogroot]/.github/workflows/autodeploy.yml 里面输入

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 当有改动推送到master分支时,启动Action
name: 自动部署

on:
push:
branches:
- master #2020年10月后github新建仓库默认分支改为main,注意更改

release:
types:
- published

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 检查分支
uses: actions/checkout@v2
with:
ref: master #2020年10月后github新建仓库默认分支改为main,注意更改

- name: 安装 Node
uses: actions/setup-node@v1
with:
node-version: "12.x"

- name: 安装 Hexo
run: |
export TZ='Asia/Shanghai'
npm install hexo-cli -g

- name: 缓存 Hexo
uses: actions/cache@v1
id: cache
with:
path: node_modules
key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}

- name: 安装依赖
if: steps.cache.outputs.cache-hit != 'true'
run: |
npm install --save

- name: 生成静态文件
run: |
hexo clean
hexo generate

- name: 部署
run: |
git config --global user.name "[GithubUsername]"
git config --global user.email "[GithubEmail]"
git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git
# 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患
# =====注意.deploy_git前面有个空格=====
# 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件
hexo deploy
\n

注意看代码里面的注释

\n
\n

重新设置远程仓库和分支

\n
    \n
  1. \n

    添加屏蔽项
    \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。
    \n打开 [Blogroot]/.gitignore, 输入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .DS_Store
    Thumbs.db
    db.json
    *.log
    node_modules/
    public/
    .deploy*/
    .deploy_git*/
    .idea
    themes/butterfly/.git
    \n

    如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题

    \n
  2. \n
  3. \n

    提交源码到刚才新建的私有仓库

    \n

    在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    git remote rm origin # 删除原有仓库链接
    git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为新的存放源码的github私有仓库
    git checkout -b master # 切换到master分支,
    #2020年10月后github新建仓库默认分支改为main,注意更改
    # 如果不是,后面的所有设置的分支记得保持一致
    git add .
    git commit -m "github action update"
    git push origin master

    #2020年10月后github新建仓库默认分支改为main,注意更改
    \n
  4. \n
\n
\n

可能遇到的 bug
\n因为 butterfly 主题文件夹下的.git 文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 themes 文件夹移动到别的目录下。然后 commit 一次。接着将 themes 文件夹移动回来,再 commit 一次。

\n
    \n
  1. \n

    删除或者先把 [Blogroot]/themes/butterfly/.git 移动到非博客文件夹目录下,原因是主题文件夹下的.git 文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。

    \n
  2. \n
  3. \n

    在博客根目录 [Blogroot] 路径下运行指令

    \n
    1
    2
    3
    4
    5
    git init #初始化
    git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为存放源码的github私有仓库
    git checkout -b master # 切换到master分支,
    #2020年10月后github新建仓库默认分支改为main,注意更改
    # 如果不是,后面的所有设置的分支记得保持一致
    \n
  4. \n
  5. \n

    添加屏蔽项
    \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。
    \n打开 [Blogroot]/.gitignore, 输入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .DS_Store
    Thumbs.db
    db.json
    *.log
    node_modules/
    public/
    .deploy*/
    .deploy_git*/
    .idea
    themes/butterfly/.git # 如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题
    \n
  6. \n
\n

完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署

\n
1
2
3
4
git add .
git commit -m "github action update"
git push origin master
#2020年10月后github新建仓库默认分支改为main,注意更改
\n

此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的.git 放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)

\n

查看部署情况

\n

\"image-20210913232006065\"

\n

点击正在运行的workflow就可以查看部署情况

\n

\"image-20210913233828678\"

\n

\"image-20210913234018254\"

\n

我遇到的Bug

\n

Bug:

\n

Spawn Failed

\n

原因主要就是配置deploy项时仓库的git链接没有正确配置,比如 :

\n
    \n
  1. gitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的)
  2. \n
  3. 配置coding时没有仔细看教程,用户名必须要使用生成 token 时系统同时生成的令牌用户名
  4. \n
\n

看板娘不能显示

\n

删除 BlogRoot/themes/butterfly/source/live2d-widget 下的 .git 文件

\n

然后执行:

\n
1
2
3
4
git rm --cached /themes/butterfly/source/live2d-widget
git add .
git commit -m "修复看板娘不能正常显示"
git push origin master
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

写在前面

\n

没用过Actions的建议细看,好好理解其工作原理和流程

\n

本文大部分参考 [糖果屋] 的教程,详见糖果屋-使用Github Action实现全自动部署, 感谢大佬!

\n

Github Actions简介

\n

Github Actions 可以理解为一个自动化脚本,当它检测到某个仓库发生变化后,自动执行脚本语句

\n

在这里可以让脚本自动执行 hexo cl && hexo g -d 三连操作

\n

获取令牌(Token)

\n

为了确保交由 Github Action 来持续部署时,Github Action 具备足够的权限来进行 hexo deploy 操作[因为远程服务器的git不能使用ssh方式推送],需要先获取 Token,博主分别在 GithubGiteeCoding 处部署了静态页面,所以也就需要获取这三处的 Token

\n

当我们获取了token后,在 _config.yml文件的 deploy 项就可以使用了

\n

访问 Github-> 头像(右上角)->Settings->Developer Settings->Personal access tokens->generate new token, 创建的 Token 名称随意,但必须勾选 repo 项。

\n

\"image-20210913220005116\"

\n

\"image-20210913220422469\"

\n

token 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token。之后如果忘记了就只能重新生成重新配置了。

\n

访问 Gitee-> 头像(右上角)-> 设置 -> 私人令牌 -> 生成新令牌

\n

\"image-20210913221257282\"

\n

token 只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token。之后如果忘记了就只能重新生成重新配置了。

\n

访问 Coding-> 头像(右上角)-> 个人账户设置 -> 访问令牌 -> 新建令牌。

\n

\"image-20210913221822733\"

\n

\"image-20210913221754924\"

\n

注意coding平台与Github、Gitee不同,它生成的令牌还有【令牌用户名】,并且只会显示这一次,之后将无法查看,所以务必保证你已经记录下了 Token令牌用户名。之后如果忘记了就只能重新生成重新配置了。

\n
\n

创建存放源码的私有仓库

\n
展开 \n
\n

我们需要创建一个用来存放 Hexo 博客源码的私有仓库 [SourceRepo],这点在 Win10Hexo 博客搭建教程中有提到。为了保持教程的连贯,此处再写一遍。
\"img\"

创建完成后,需要把博客的源码 push 到这里。首先获取远程仓库地址,此处虽然 SSHHTTPS 均可。SSH 在绑定过 ssh key 的设备上无需再输入密码,HTTPS 则需要输入密码,但是 SSH 偶尔会遇到端口占用的情况。请自主选择。
\"img\"

这里之所以是私有仓库,是因为在接下来的配置中会用到 Token,如果 Token 被盗用,别人可以肆意操作你的 github 仓库内容,为了避免这一风险,才选择的博客源码闭源。

\n
\n
\n

配置 deploy 项

\n

打开站点配置文件 [Blogroot]/_config.yml, 找到 deploy 配置项,使用之前生成的 [SiteToken] 和各个站点仓库 URL 来组装地址。

\n
1
2
3
4
5
6
7
8
deploy:
- type: git
repo:
gitHub: https://[GithubUsername]:[GithubToken]@github.com/[GithubUsername]/[GithubBlogRepo].git[,branch]
gitee: https://[Gitee用户名]:[GiteeToken]@gitee.com/[GiteeUsername]/[GiteeBlogRepo].git[,branch]
coding: https://[令牌用户名]:[CodingToken]@e.coding.net/[CodingUsername]/[CodingBlogRepo].git[,branch]
# [,branch]为可选项,表示部署的分支
#2020年10月后github新建仓库默认分支改为main,注意更改
\n

注意:Gitee的用户名如果含有大写字母,要改为小写; coding的【令牌用户名】是生成令牌时显示出来的,如果没有注意,要重新生成一次

\n
\n

参考我个人的写法:

\n
1
2
3
4
5
6
deploy:
type: git
repo:
github: https://aJream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@github.com/aJream/aJream.github.io.git,master
gitee: https://ajream:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@gitee.com/ajream/ajream.git,master
coding: https://xxxxxxxxxx:XXXXXXXXXXXXXXXXXXXXXXXXXXXX@e.coding.net/ajream/page/blog1.git,master
\n

配置 Github Action

\n

[Blogroot] 新建.github 文件夹,注意开头是有个. 的。然后在.github 内新建 workflows 文件夹,再在 workflows 文件夹内新建 autodeploy.yml, 在 [Blogroot]/.github/workflows/autodeploy.yml 里面输入

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 当有改动推送到master分支时,启动Action
name: 自动部署

on:
push:
branches:
- master #2020年10月后github新建仓库默认分支改为main,注意更改

release:
types:
- published

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 检查分支
uses: actions/checkout@v2
with:
ref: master #2020年10月后github新建仓库默认分支改为main,注意更改

- name: 安装 Node
uses: actions/setup-node@v1
with:
node-version: "12.x"

- name: 安装 Hexo
run: |
export TZ='Asia/Shanghai'
npm install hexo-cli -g

- name: 缓存 Hexo
uses: actions/cache@v1
id: cache
with:
path: node_modules
key: ${{runner.OS}}-${{hashFiles('**/package-lock.json')}}

- name: 安装依赖
if: steps.cache.outputs.cache-hit != 'true'
run: |
npm install --save

- name: 生成静态文件
run: |
hexo clean
hexo generate

- name: 部署
run: |
git config --global user.name "[GithubUsername]"
git config --global user.email "[GithubEmail]"
git clone https://github.com/[GithubUsername]/[GithubBlogRepo].git .deploy_git
# 此处务必用HTTPS链接。SSH链接可能有权限报错的隐患
# =====注意.deploy_git前面有个空格=====
# 这行指令的目的是clone博客静态文件仓库,防止Hexo推送时覆盖整个静态文件仓库,而是只推送有更改的文件
hexo deploy
\n

注意看代码里面的注释

\n
\n

重新设置远程仓库和分支

\n
    \n
  1. \n

    添加屏蔽项
    \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。
    \n打开 [Blogroot]/.gitignore, 输入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .DS_Store
    Thumbs.db
    db.json
    *.log
    node_modules/
    public/
    .deploy*/
    .deploy_git*/
    .idea
    themes/butterfly/.git
    \n

    如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题

    \n
  2. \n
  3. \n

    提交源码到刚才新建的私有仓库

    \n

    在博客根目录 [Blogroot] 下启动终端,使用 git 指令重设仓库地址。这样在新建仓库,我们仍旧可以保留珍贵的 commit history,便于版本回滚。

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    git remote rm origin # 删除原有仓库链接
    git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为新的存放源码的github私有仓库
    git checkout -b master # 切换到master分支,
    #2020年10月后github新建仓库默认分支改为main,注意更改
    # 如果不是,后面的所有设置的分支记得保持一致
    git add .
    git commit -m "github action update"
    git push origin master

    #2020年10月后github新建仓库默认分支改为main,注意更改
    \n
  4. \n
\n
\n

可能遇到的 bug
\n因为 butterfly 主题文件夹下的.git 文件夹的存在,那么主题文件夹会被识别子项目。从而无法被上传到源码仓库。若是遇到添加屏蔽项,但是还是无法正常上传主题文件夹的情况。请先将本地源码中的 themes 文件夹移动到别的目录下。然后 commit 一次。接着将 themes 文件夹移动回来,再 commit 一次。

\n
    \n
  1. \n

    删除或者先把 [Blogroot]/themes/butterfly/.git 移动到非博客文件夹目录下,原因是主题文件夹下的.git 文件夹的存在会导致其被识别成子项目,从而无法被上传到源码仓库。

    \n
  2. \n
  3. \n

    在博客根目录 [Blogroot] 路径下运行指令

    \n
    1
    2
    3
    4
    5
    git init #初始化
    git remote add origin git@github.com:[Github用户名]/[私有仓库名].git #[SourceRepo]为存放源码的github私有仓库
    git checkout -b master # 切换到master分支,
    #2020年10月后github新建仓库默认分支改为main,注意更改
    # 如果不是,后面的所有设置的分支记得保持一致
    \n
  4. \n
  5. \n

    添加屏蔽项
    \n因为能够使用指令进行安装的内容不包括在需要提交的源码内,所有我们需要将这些内容添加到屏蔽项,表示不上传到 github 上。这样可以显著减少需要提交的文件量和加快提交速度。
    \n打开 [Blogroot]/.gitignore, 输入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .DS_Store
    Thumbs.db
    db.json
    *.log
    node_modules/
    public/
    .deploy*/
    .deploy_git*/
    .idea
    themes/butterfly/.git # 如果不是 butterfly 主题,记得替换最后一行内容为你自己当前使用的主题
    \n
  6. \n
\n

完成后就可以用git指令进行提交到Github源码仓库,Actions监测到仓库发生改变会自动部署

\n
1
2
3
4
git add .
git commit -m "github action update"
git push origin master
#2020年10月后github新建仓库默认分支改为main,注意更改
\n

此时你的主题文件夹若已经被正常上传,并且你也添加了主题文件夹下的.git 文件夹的屏蔽项。那可以考虑把第二步移走或删除的.git 放回来,用作以后升级。(不禁怀疑真的有人会去用这个方式来升级吗)

\n

查看部署情况

\n

\"image-20210913232006065\"

\n

点击正在运行的workflow就可以查看部署情况

\n

\"image-20210913233828678\"

\n

\"image-20210913234018254\"

\n

我遇到的Bug

\n

Bug:

\n

Spawn Failed

\n

原因主要就是配置deploy项时仓库的git链接没有正确配置,比如 :

\n
    \n
  1. gitee仓库的用户名使用大写就不行,要改为小写(平台上的用户名不用改,只需要改链接中的)
  2. \n
  3. 配置coding时没有仔细看教程,用户名必须要使用生成 token 时系统同时生成的令牌用户名
  4. \n
\n

看板娘不能显示

\n

删除 BlogRoot/themes/butterfly/source/live2d-widget 下的 .git 文件

\n

然后执行:

\n
1
2
3
4
git rm --cached /themes/butterfly/source/live2d-widget
git add .
git commit -m "修复看板娘不能正常显示"
git push origin master
\n"},{"title":"51单片机学习笔记(四)","description":"串行通信中的IIC总线工作原理和协议+EEPROM","abbrlink":"c27dd6ec","date":"2021-08-19T08:07:10.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n\n\n目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线\n\n- UART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。\n\n- 1-wire:即单线总线,又叫单总线(只有一条线)。\n\n- I2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。\n\n- SPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。\n\n\n\n## 原理\n\nIIC串行总线,只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL\n\n如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据\n\n![image-20210822161125324](https://gitee.com/ajream/images/raw/master/img/20210822161128_image-20210822161125324.png)\n\n在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。\n\n在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况\n\n\n\n总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,\n\n即`SDA = SDA1 & SDA2 & SDA3 & ...`, `SCL=SCL1 & SCL2 & SCL3 & ...`\n\n![image-20210822161725608](https://gitee.com/ajream/images/raw/master/img/20210822161726_image-20210822161725608.png)\n\n\n\n\n\n### 起始信号和终止信号\n\nSCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;\n\nSCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。\n\n![image-20210822162305371](https://gitee.com/ajream/images/raw/master/img/20210822162306_image-20210822162305371.png)\n\n### 数据位有效性\n\nSCL为高电平期间,数据线上的数据必须保持稳定\n\n只有SCL信号为低电平期间,SDA状态才允许变化。\n\n![image-20210822162522027](https://gitee.com/ajream/images/raw/master/img/20210822162523_image-20210822162522027.png)\n\n### IIC总线的传送与应答\n\n每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节**最后面**都必须跟随一位应答位(即一帧共有9位)\n\n主机通过从机发出的**应答位**来判断从机是否成功接收数据:\n\n- 从机正忙于其他事情,发出应答1,表示没有收到\n- 当从机空闲可以接收该字节数据时,从机会发出应答0\n\n当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。\n\n\n\n### 数据帧格式\n\n在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用\"0\"表示主机发送数据(T),\"1\"表示主机接收数据(R)。\n\n每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。\n\n### 软件模拟IIC总线传送数据\n\n主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。\n\n为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :\n\n![image-20210822163923932](https://gitee.com/ajream/images/raw/master/img/20210822163925_image-20210822163923932.png)\n\n模拟的代码如下:\n\n```c\n//起始信号\nvoid I2cStart(){\n\tSCL = 1;\n\tSDA = 1;\n\tdelay5us(); //SDA在拉低低之前,应该先保持高电平时间>4us,这里用5us\n\tSDA = 0;\n\tdelay5us();\t //SDA保持低电平时间>4us,这里用5us\n}\n\n//终止信号\nvoid I2cStop(){\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();\n\tSDA = 1;\n\tdelay5us();\n}\n\n```\n\n第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下\n\n\n```c\n//主机读取从机的应答\nbit ReadACK(){\n\tSCL = 1;\n\tdelay5us();\n\tif(SDA){\n\t\tSCL = 0;\n\t\treturn(1);\n\t}\n\telse{\n\t\tSCL = 0;\n\t\treturn(0);\n\t}\n}\n\n\n```\n\n\n\n发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线\n\n```c\n//主机发送应答\nvoid SendACK(bit i){\n\tSCL = 0;\n \n\tif(i) SDA = 1;\n\telse SDA = 0;\n \n\tSCL = 1;\n\tdelay5us();\n\tSCL = 0; \t //拉低时钟总线\n\tSDA = 1;\t //释放数据总线\n}\n```\n\n\n\n## 串行EEPROM的扩展\n\nAteml公司的AT24C系列:\n\n- AT24C01:128字节(128×8位)\n\n- AT24C02:256字节(256×8位)\n- AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位)\n- AT24C16:2K字节(2K×8位)\n\n\n\n### 写入过程\n\n**获取地址码:**AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 `1010000`\n\n![image-20210824140820582](https://gitee.com/ajream/images/raw/master/img/20210824140829_image-20210824140820582.png)\n\n**写操作:**单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。\n\n\n\n**传送数据:**\n\n- 这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。\n\n- AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)\n\n- 当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:\n\n ![image-20210822171711924](https://gitee.com/ajream/images/raw/master/img/20210822171714_image-20210822171711924.png)\n \n > 说明:S为起始信号,然后是7位地址码+ `0`(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。\n \n- 注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。\n\n\n\n### 读出过程\n\n单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。 \n\n\n\n然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。\n\n当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。 \n\n![image-20210822171925336](https://gitee.com/ajream/images/raw/master/img/20210822171926_image-20210822171925336.png)\n\n\n\n完整代码:\n\n```c\n/*********************************************************************************\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tIIC通信,AT24C02读写数据,数码管显示数据。\t\t \t\t\t\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n#define At24c02ADDR 0XA0 //AT24C02硬件地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1;//I2C时钟总线\nsbit SDA = P2^0;//I2C数据总线\nuchar num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n/*====================================\n函数\t: delay(uint z)\n参数\t:z 延时毫秒设定,取值范围0-65535\n返回值\t:无\n描述\t:12T/Fosc11.0592M毫秒级延时\n====================================*/\nvoid delay(uint z){\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示数值,取值范围0-255\n返回值\t:无\n描述\t:三位共阴数码管动态显示\n====================================*/\nvoid display(uchar i){\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei){\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid timer0Init(){\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:delay5us()\n参数\t:无\n返回值\t:无\n描述\t:5us延时函数\n====================================*/\nvoid delay5us(){\n\t_nop_();\n}\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart(){\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop(){\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK(){\n\tSCL = 0;\t\t\t//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;\t\t\t//拉高,读SDA\n\tdelay5us();\n\tif(SDA){\t\t\t//NOACK\n\t\tSCL = 0;\n\t\treturn(1);\t\t//返回1\n\t}\n\telse{\t\t\t\t//ACK\n\t\tSCL = 0;\n\t\treturn(0);\t\t//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i){\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT){\n\tuchar i; \n\tfor(i=0; i<8; i++){ //分别写8次,每次写1位\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:At24c02Write(uchar ADDR, DAT)\n参数\t:ADDR 单元地址0-255,DAT 需要输入的数据0-255\n返回值\t:无\n描述\t:At24c02指定单元写入一个字节数据\n====================================*/\nvoid At24c02Write(uchar ADDR, DAT){\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK()) //读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//发送储存单元地址字节\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(DAT);//发送一字节数据\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cStop();\t//I2C停止信号\n}\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte(){\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++){ \t//分别读8次,每次读一位\n\t\n\t\tDAT <<= 1; \t\t\t//数据左移1位,准备接收一位\n\t\tSCL = 0; \t\t\t//拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; \t\t\t//拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;\t//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); \t\t\t//返回读出的数据\n}\n\n/*====================================\n函数\t:At24c02Read(uchar ADDR)\n参数\t:ADDR 单元地址\t0-255\n返回值\t:返回指定单元的数据\n描述\t:读AT24C02指定单元内数据\n====================================*/\nuchar At24c02Read(uchar ADDR){\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//I2C发送一个字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环{\t\n\ttimer0Init();//定时器0初始化\n\tEA = 0;//屏蔽中断\n\tAt24c02Write(3, 188);//给第3单元写入数据“188”\n\tdelay(1);//延时等待AT24C02处理\n\tnum = At24c02Read(3);//读出第3单元内数据送给显示变量\n\tif(AckFlag)//当从机非应答\n\t\tP1 = 0;//亮P1所有灯\n\telse\n\t\tP1 = 0XFF;//灭P1所有灯\n\tEA = 1;//开中断\n\twhile(1);\n} \n\n//定时器0中断函数\nvoid timer0() interrupt 1 {\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(四).md","raw":"---\ntitle: 51单片机学习笔记(四)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 串行通信中的IIC总线工作原理和协议+EEPROM\nabbrlink: c27dd6ec\ndate: 2021-08-19 16:07:10\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n\n\n目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线\n\n- UART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。\n\n- 1-wire:即单线总线,又叫单总线(只有一条线)。\n\n- I2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。\n\n- SPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。\n\n\n\n## 原理\n\nIIC串行总线,只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL\n\n如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据\n\n![image-20210822161125324](https://gitee.com/ajream/images/raw/master/img/20210822161128_image-20210822161125324.png)\n\n在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。\n\n在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况\n\n\n\n总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,\n\n即`SDA = SDA1 & SDA2 & SDA3 & ...`, `SCL=SCL1 & SCL2 & SCL3 & ...`\n\n![image-20210822161725608](https://gitee.com/ajream/images/raw/master/img/20210822161726_image-20210822161725608.png)\n\n\n\n\n\n### 起始信号和终止信号\n\nSCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;\n\nSCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。\n\n![image-20210822162305371](https://gitee.com/ajream/images/raw/master/img/20210822162306_image-20210822162305371.png)\n\n### 数据位有效性\n\nSCL为高电平期间,数据线上的数据必须保持稳定\n\n只有SCL信号为低电平期间,SDA状态才允许变化。\n\n![image-20210822162522027](https://gitee.com/ajream/images/raw/master/img/20210822162523_image-20210822162522027.png)\n\n### IIC总线的传送与应答\n\n每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节**最后面**都必须跟随一位应答位(即一帧共有9位)\n\n主机通过从机发出的**应答位**来判断从机是否成功接收数据:\n\n- 从机正忙于其他事情,发出应答1,表示没有收到\n- 当从机空闲可以接收该字节数据时,从机会发出应答0\n\n当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。\n\n\n\n### 数据帧格式\n\n在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用\"0\"表示主机发送数据(T),\"1\"表示主机接收数据(R)。\n\n每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。\n\n### 软件模拟IIC总线传送数据\n\n主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。\n\n为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :\n\n![image-20210822163923932](https://gitee.com/ajream/images/raw/master/img/20210822163925_image-20210822163923932.png)\n\n模拟的代码如下:\n\n```c\n//起始信号\nvoid I2cStart(){\n\tSCL = 1;\n\tSDA = 1;\n\tdelay5us(); //SDA在拉低低之前,应该先保持高电平时间>4us,这里用5us\n\tSDA = 0;\n\tdelay5us();\t //SDA保持低电平时间>4us,这里用5us\n}\n\n//终止信号\nvoid I2cStop(){\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();\n\tSDA = 1;\n\tdelay5us();\n}\n\n```\n\n第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下\n\n\n```c\n//主机读取从机的应答\nbit ReadACK(){\n\tSCL = 1;\n\tdelay5us();\n\tif(SDA){\n\t\tSCL = 0;\n\t\treturn(1);\n\t}\n\telse{\n\t\tSCL = 0;\n\t\treturn(0);\n\t}\n}\n\n\n```\n\n\n\n发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线\n\n```c\n//主机发送应答\nvoid SendACK(bit i){\n\tSCL = 0;\n \n\tif(i) SDA = 1;\n\telse SDA = 0;\n \n\tSCL = 1;\n\tdelay5us();\n\tSCL = 0; \t //拉低时钟总线\n\tSDA = 1;\t //释放数据总线\n}\n```\n\n\n\n## 串行EEPROM的扩展\n\nAteml公司的AT24C系列:\n\n- AT24C01:128字节(128×8位)\n\n- AT24C02:256字节(256×8位)\n- AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位)\n- AT24C16:2K字节(2K×8位)\n\n\n\n### 写入过程\n\n**获取地址码:**AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 `1010000`\n\n![image-20210824140820582](https://gitee.com/ajream/images/raw/master/img/20210824140829_image-20210824140820582.png)\n\n**写操作:**单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。\n\n\n\n**传送数据:**\n\n- 这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。\n\n- AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)\n\n- 当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:\n\n ![image-20210822171711924](https://gitee.com/ajream/images/raw/master/img/20210822171714_image-20210822171711924.png)\n \n > 说明:S为起始信号,然后是7位地址码+ `0`(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。\n \n- 注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。\n\n\n\n### 读出过程\n\n单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。 \n\n\n\n然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。\n\n当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。 \n\n![image-20210822171925336](https://gitee.com/ajream/images/raw/master/img/20210822171926_image-20210822171925336.png)\n\n\n\n完整代码:\n\n```c\n/*********************************************************************************\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tIIC通信,AT24C02读写数据,数码管显示数据。\t\t \t\t\t\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n#define At24c02ADDR 0XA0 //AT24C02硬件地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1;//I2C时钟总线\nsbit SDA = P2^0;//I2C数据总线\nuchar num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n/*====================================\n函数\t: delay(uint z)\n参数\t:z 延时毫秒设定,取值范围0-65535\n返回值\t:无\n描述\t:12T/Fosc11.0592M毫秒级延时\n====================================*/\nvoid delay(uint z){\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示数值,取值范围0-255\n返回值\t:无\n描述\t:三位共阴数码管动态显示\n====================================*/\nvoid display(uchar i){\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei){\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid timer0Init(){\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:delay5us()\n参数\t:无\n返回值\t:无\n描述\t:5us延时函数\n====================================*/\nvoid delay5us(){\n\t_nop_();\n}\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart(){\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop(){\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK(){\n\tSCL = 0;\t\t\t//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;\t\t\t//拉高,读SDA\n\tdelay5us();\n\tif(SDA){\t\t\t//NOACK\n\t\tSCL = 0;\n\t\treturn(1);\t\t//返回1\n\t}\n\telse{\t\t\t\t//ACK\n\t\tSCL = 0;\n\t\treturn(0);\t\t//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i){\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT){\n\tuchar i; \n\tfor(i=0; i<8; i++){ //分别写8次,每次写1位\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:At24c02Write(uchar ADDR, DAT)\n参数\t:ADDR 单元地址0-255,DAT 需要输入的数据0-255\n返回值\t:无\n描述\t:At24c02指定单元写入一个字节数据\n====================================*/\nvoid At24c02Write(uchar ADDR, DAT){\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK()) //读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//发送储存单元地址字节\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(DAT);//发送一字节数据\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cStop();\t//I2C停止信号\n}\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte(){\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++){ \t//分别读8次,每次读一位\n\t\n\t\tDAT <<= 1; \t\t\t//数据左移1位,准备接收一位\n\t\tSCL = 0; \t\t\t//拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; \t\t\t//拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;\t//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); \t\t\t//返回读出的数据\n}\n\n/*====================================\n函数\t:At24c02Read(uchar ADDR)\n参数\t:ADDR 单元地址\t0-255\n返回值\t:返回指定单元的数据\n描述\t:读AT24C02指定单元内数据\n====================================*/\nuchar At24c02Read(uchar ADDR){\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(ADDR);//I2C发送一个字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环{\t\n\ttimer0Init();//定时器0初始化\n\tEA = 0;//屏蔽中断\n\tAt24c02Write(3, 188);//给第3单元写入数据“188”\n\tdelay(1);//延时等待AT24C02处理\n\tnum = At24c02Read(3);//读出第3单元内数据送给显示变量\n\tif(AckFlag)//当从机非应答\n\t\tP1 = 0;//亮P1所有灯\n\telse\n\t\tP1 = 0XFF;//灭P1所有灯\n\tEA = 1;//开中断\n\twhile(1);\n} \n\n//定时器0中断函数\nvoid timer0() interrupt 1 {\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(四)","published":1,"updated":"2021-09-10T12:45:36.194Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6og000vakve4p1e67aw","content":"

目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线

\n
    \n
  • UART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。

    \n
  • \n
  • 1-wire:即单线总线,又叫单总线(只有一条线)。

    \n
  • \n
  • I2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。

    \n
  • \n
  • SPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。

    \n
  • \n
\n

原理

IIC串行总线,只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL

\n

如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据

\n

\"image-20210822161125324\"

\n

在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。

\n

在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况

\n

总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,

\n

SDA = SDA1 & SDA2 & SDA3 & ..., SCL=SCL1 & SCL2 & SCL3 & ...

\n

\"image-20210822161725608\"

\n

起始信号和终止信号

SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;

\n

SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。

\n

\"image-20210822162305371\"

\n

数据位有效性

SCL为高电平期间,数据线上的数据必须保持稳定

\n

只有SCL信号为低电平期间,SDA状态才允许变化。

\n

\"image-20210822162522027\"

\n

IIC总线的传送与应答

每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节最后面都必须跟随一位应答位(即一帧共有9位)

\n

主机通过从机发出的应答位来判断从机是否成功接收数据:

\n
    \n
  • 从机正忙于其他事情,发出应答1,表示没有收到
  • \n
  • 当从机空闲可以接收该字节数据时,从机会发出应答0
  • \n
\n

当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。

\n

数据帧格式

在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用”0”表示主机发送数据(T),”1”表示主机接收数据(R)。

\n

每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。

\n

软件模拟IIC总线传送数据

主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。

\n

为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :

\n

\"image-20210822163923932\"

\n

模拟的代码如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//起始信号
void I2cStart(){
\tSCL = 1;
\tSDA = 1;
\tdelay5us(); //SDA在拉低低之前,应该先保持高电平时间>4us,这里用5us
\tSDA = 0;
\tdelay5us();\t //SDA保持低电平时间>4us,这里用5us
}

//终止信号
void I2cStop(){
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();
\tSDA = 1;
\tdelay5us();
}

\n

第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//主机读取从机的应答
bit ReadACK(){
\tSCL = 1;
\tdelay5us();
\tif(SDA){
\t\tSCL = 0;
\t\treturn(1);
\t}
\telse{
\t\tSCL = 0;
\t\treturn(0);
\t}
}


\n

发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线

\n
1
2
3
4
5
6
7
8
9
10
11
12
//主机发送应答
void SendACK(bit i){
\tSCL = 0;

\tif(i) SDA = 1;
\telse SDA = 0;

\tSCL = 1;
\tdelay5us();
\tSCL = 0; \t //拉低时钟总线
\tSDA = 1;\t //释放数据总线
}
\n

串行EEPROM的扩展

Ateml公司的AT24C系列:

\n
    \n
  • AT24C01:128字节(128×8位)

    \n
  • \n
  • AT24C02:256字节(256×8位)

    \n
  • \n
  • AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位)
  • \n
  • AT24C16:2K字节(2K×8位)
  • \n
\n

写入过程

获取地址码:AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 1010000

\n

\"image-20210824140820582\"

\n

写操作:单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。

\n

传送数据:

\n
    \n
  • 这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。

    \n
  • \n
  • AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)

    \n
  • \n
  • 当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:

    \n

    \"image-20210822171711924\"

    \n
    \n

    说明:S为起始信号,然后是7位地址码+ 0(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。

    \n
    \n
  • \n
  • 注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。

    \n
  • \n
\n

读出过程

单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。

\n

然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。

\n

当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。

\n

\"image-20210822171925336\"

\n

完整代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/*********************************************************************************
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tIIC通信,AT24C02读写数据,数码管显示数据。\t\t \t\t\t
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char
#define At24c02ADDR 0XA0 //AT24C02硬件地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1;//I2C时钟总线
sbit SDA = P2^0;//I2C数据总线
uchar num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

/*====================================
函数\t: delay(uint z)
参数\t:z 延时毫秒设定,取值范围0-65535
返回值\t:无
描述\t:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z){
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示数值,取值范围0-255
返回值\t:无
描述\t:三位共阴数码管动态显示
====================================*/
void display(uchar i){
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei){
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void timer0Init(){
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}
/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:delay5us()
参数\t:无
返回值\t:无
描述\t:5us延时函数
====================================*/
void delay5us(){
\t_nop_();
}

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart(){
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop(){
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK(){
\tSCL = 0;\t\t\t//拉低时钟总线,允许从机控制SDA
\tSCL = 1;\t\t\t//拉高,读SDA
\tdelay5us();
\tif(SDA){\t\t\t//NOACK
\t\tSCL = 0;
\t\treturn(1);\t\t//返回1
\t}
\telse{\t\t\t\t//ACK
\t\tSCL = 0;
\t\treturn(0);\t\t//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i){
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT){
\tuchar i;
\tfor(i=0; i<8; i++){ //分别写8次,每次写1位
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:At24c02Write(uchar ADDR, DAT)
参数\t:ADDR 单元地址0-255,DAT 需要输入的数据0-255
返回值\t:无
描述\t:At24c02指定单元写入一个字节数据
====================================*/
void At24c02Write(uchar ADDR, DAT){
\tI2cStart();//I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK()) //读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(ADDR);//发送储存单元地址字节
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(DAT);//发送一字节数据
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cStop();\t//I2C停止信号
}

/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte(){
\tuchar i, DAT;
\tfor(i=0; i<8; i++){ \t//分别读8次,每次读一位
\t
\t\tDAT <<= 1; \t\t\t//数据左移1位,准备接收一位
\t\tSCL = 0; \t\t\t//拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; \t\t\t//拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;\t//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); \t\t\t//返回读出的数据
}

/*====================================
函数\t:At24c02Read(uchar ADDR)
参数\t:ADDR 单元地址\t0-255
返回值\t:返回指定单元的数据
描述\t:读AT24C02指定单元内数据
====================================*/
uchar At24c02Read(uchar ADDR){
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(ADDR);//I2C发送一个字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环{\t
\ttimer0Init();//定时器0初始化
\tEA = 0;//屏蔽中断
\tAt24c02Write(3, 188);//给第3单元写入数据“188”
\tdelay(1);//延时等待AT24C02处理
\tnum = At24c02Read(3);//读出第3单元内数据送给显示变量
\tif(AckFlag)//当从机非应答
\t\tP1 = 0;//亮P1所有灯
\telse
\t\tP1 = 0XFF;//灭P1所有灯
\tEA = 1;//开中断
\twhile(1);
}

//定时器0中断函数
void timer0() interrupt 1 {
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

目前常用的微机与外设之间进行数据传输的串行总线主要有UART、1-wire、I2C和SPI总线

\n
    \n
  • UART:是以异步方式进行通信(一条数据输入线,一条数据输出线)。

    \n
  • \n
  • 1-wire:即单线总线,又叫单总线(只有一条线)。

    \n
  • \n
  • I2C:同步串行2线方式进行通信(一条时钟线,一条数据线)。

    \n
  • \n
  • SPI:同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。

    \n
  • \n
\n

原理

IIC串行总线,只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL

\n

如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据

\n

\"image-20210822161125324\"

\n

在多主机系统中,可能同时有几个主机企图启动总线传送数据。为了避免混乱,I2C总线要通过总线仲裁,以决定由哪一台主机控制总线。

\n

在80C51单片机应用系统的串行总线扩展中,我们经常遇到的是以80C51单片机为主机,其它接口器件为从机的单主机情况

\n

总线“线与关系”:I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,

\n

SDA = SDA1 & SDA2 & SDA3 & ..., SCL=SCL1 & SCL2 & SCL3 & ...

\n

\"image-20210822161725608\"

\n

起始信号和终止信号

SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;

\n

SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。

\n

\"image-20210822162305371\"

\n

数据位有效性

SCL为高电平期间,数据线上的数据必须保持稳定

\n

只有SCL信号为低电平期间,SDA状态才允许变化。

\n

\"image-20210822162522027\"

\n

IIC总线的传送与应答

每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节最后面都必须跟随一位应答位(即一帧共有9位)

\n

主机通过从机发出的应答位来判断从机是否成功接收数据:

\n
    \n
  • 从机正忙于其他事情,发出应答1,表示没有收到
  • \n
  • 当从机空闲可以接收该字节数据时,从机会发出应答0
  • \n
\n

当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。

\n

数据帧格式

在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用”0”表示主机发送数据(T),”1”表示主机接收数据(R)。

\n

每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。

\n

软件模拟IIC总线传送数据

主机可以采用不带IIC总线接口的单片机,如80C51、STC89C52等单片机,利用软件实现IIC总线的数据传送,即软件与硬件结合的信号模拟。

\n

为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :

\n

\"image-20210822163923932\"

\n

模拟的代码如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//起始信号
void I2cStart(){
\tSCL = 1;
\tSDA = 1;
\tdelay5us(); //SDA在拉低低之前,应该先保持高电平时间>4us,这里用5us
\tSDA = 0;
\tdelay5us();\t //SDA保持低电平时间>4us,这里用5us
}

//终止信号
void I2cStop(){
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();
\tSDA = 1;
\tdelay5us();
}

\n

第3、4个图表示先将SCL拉高5us,然后读取并返回SDA的值,读取后将SCL拉低,代码如下

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//主机读取从机的应答
bit ReadACK(){
\tSCL = 1;
\tdelay5us();
\tif(SDA){
\t\tSCL = 0;
\t\treturn(1);
\t}
\telse{
\t\tSCL = 0;
\t\treturn(0);
\t}
}


\n

发送数据i,在SCL为低时,将SDA拉高或拉低(根据发送的数据确定,如果发送1,则拉高,反之拉低);然后将SCL拉高,保持5us,再将SCL拉低,这样就发送了一位(bit)数据,最后释放总线

\n
1
2
3
4
5
6
7
8
9
10
11
12
//主机发送应答
void SendACK(bit i){
\tSCL = 0;

\tif(i) SDA = 1;
\telse SDA = 0;

\tSCL = 1;
\tdelay5us();
\tSCL = 0; \t //拉低时钟总线
\tSDA = 1;\t //释放数据总线
}
\n

串行EEPROM的扩展

Ateml公司的AT24C系列:

\n
    \n
  • AT24C01:128字节(128×8位)

    \n
  • \n
  • AT24C02:256字节(256×8位)

    \n
  • \n
  • AT24C04:512字节(512×8位)AT24C08:1K字节(1K×8位)
  • \n
  • AT24C16:2K字节(2K×8位)
  • \n
\n

写入过程

获取地址码:AT24C系列E2PROM芯片地址的固定部分为1010,而当A2、A1、A0引脚接高、低电平后就得到确定的3位编码。形成的7位编码即为该器件的地址码。例如在开发板中A2, A1, A0都接地,则这7位码为 1010000

\n

\"image-20210824140820582\"

\n

写操作:单片机进行写操作时,首先发送该器件的7位地址码和写方向位“0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为相应,单片机收到应答后就可以传送数据了。

\n

传送数据:

\n
    \n
  • 这个过程单片机首先发送一个字节被写入器件的存储区首地址,收到存储器器件的应答后,单片机就逐个发送各数据字节,但每发送一个字节后都要等待应答。

    \n
  • \n
  • AT24C系列器件片内地址在接收到每一个数据字节地址后自动加1(表示相对之前写入的位置往后移一位)

    \n
  • \n
  • 当要写入的数据传送完后,单片机应发出终止信号以结束写入操作。写入n个字节的数据格式如下:

    \n

    \"image-20210822171711924\"

    \n
    \n

    说明:S为起始信号,然后是7位地址码+ 0(0表示写操作),然后写入第一个数据存放位置的首地址,后面的写入的数据自动往后移动一位,写入1Byte数据后,器件发送应答A确认已写入,最后如果写入完成后发送终止信号P。

    \n
    \n
  • \n
  • 注意:在芯片的“一次装载字节数”(不同芯片字节数不同)限度内,只需输入首地址。装载字节数超过芯片的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。

    \n
  • \n
\n

读出过程

单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。

\n

然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。

\n

当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。

\n

\"image-20210822171925336\"

\n

完整代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/*********************************************************************************
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tIIC通信,AT24C02读写数据,数码管显示数据。\t\t \t\t\t
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char
#define At24c02ADDR 0XA0 //AT24C02硬件地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1;//I2C时钟总线
sbit SDA = P2^0;//I2C数据总线
uchar num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

/*====================================
函数\t: delay(uint z)
参数\t:z 延时毫秒设定,取值范围0-65535
返回值\t:无
描述\t:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z){
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示数值,取值范围0-255
返回值\t:无
描述\t:三位共阴数码管动态显示
====================================*/
void display(uchar i){
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei){
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void timer0Init(){
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}
/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:delay5us()
参数\t:无
返回值\t:无
描述\t:5us延时函数
====================================*/
void delay5us(){
\t_nop_();
}

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart(){
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop(){
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK(){
\tSCL = 0;\t\t\t//拉低时钟总线,允许从机控制SDA
\tSCL = 1;\t\t\t//拉高,读SDA
\tdelay5us();
\tif(SDA){\t\t\t//NOACK
\t\tSCL = 0;
\t\treturn(1);\t\t//返回1
\t}
\telse{\t\t\t\t//ACK
\t\tSCL = 0;
\t\treturn(0);\t\t//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i){
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT){
\tuchar i;
\tfor(i=0; i<8; i++){ //分别写8次,每次写1位
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:At24c02Write(uchar ADDR, DAT)
参数\t:ADDR 单元地址0-255,DAT 需要输入的数据0-255
返回值\t:无
描述\t:At24c02指定单元写入一个字节数据
====================================*/
void At24c02Write(uchar ADDR, DAT){
\tI2cStart();//I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK()) //读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(ADDR);//发送储存单元地址字节
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(DAT);//发送一字节数据
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cStop();\t//I2C停止信号
}

/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte(){
\tuchar i, DAT;
\tfor(i=0; i<8; i++){ \t//分别读8次,每次读一位
\t
\t\tDAT <<= 1; \t\t\t//数据左移1位,准备接收一位
\t\tSCL = 0; \t\t\t//拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; \t\t\t//拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;\t//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); \t\t\t//返回读出的数据
}

/*====================================
函数\t:At24c02Read(uchar ADDR)
参数\t:ADDR 单元地址\t0-255
返回值\t:返回指定单元的数据
描述\t:读AT24C02指定单元内数据
====================================*/
uchar At24c02Read(uchar ADDR){
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(ADDR);//I2C发送一个字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环{\t
\ttimer0Init();//定时器0初始化
\tEA = 0;//屏蔽中断
\tAt24c02Write(3, 188);//给第3单元写入数据“188”
\tdelay(1);//延时等待AT24C02处理
\tnum = At24c02Read(3);//读出第3单元内数据送给显示变量
\tif(AckFlag)//当从机非应答
\t\tP1 = 0;//亮P1所有灯
\telse
\t\tP1 = 0XFF;//灭P1所有灯
\tEA = 1;//开中断
\twhile(1);
}

//定时器0中断函数
void timer0() interrupt 1 {
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}
\n"},{"title":"java垃圾回收算法","abbrlink":"bdfc8ea1","date":"2021-05-16T03:16:42.000Z","description":"Java垃圾回收原理解析","_content":"\n\n\n# Java垃圾回收算法\n\n[咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/73628158)\n\n## 什么是垃圾(定义)\n\n有两种算法来定义\n\n### 引用计数算法\n\n引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。\n\n> **缺点:**循环引用(其计数永远不会为0)\n\n### 可达性分析算法\n\n可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。\n\n\"image-20210423190611076\"\n\n\n\n> 可达性算法,解决了引用计数所无法解决的问题-“循环依赖”\n\n## 怎么回收(算法)\n\n### 标记-清除算法\n\n\"image-20210424225646255\"\n\n标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;\n\n缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存\n\n\n\n### 复制算法\n\n**回收前:**\n\n\"image-20210424231518421\"\n\n**回收后:**\n\n\"image-20210424231836708\"\n\n\n\n原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:\n\n1. 首先把存活的对象都复制到另一块空内存上\n2. 再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。\n\n优点:解决了标记-清除算法的问题\n\n缺点:同一时间只能使用其中一半的内存\n\n### 标记-整理算法\n\n\"img\"\n\n标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。\n\n优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。\n\n缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。\n\n\n\n### 分代收集算法\n\n分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。\n\n对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。\n\n1. 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。\n2. 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记-整理算法来进行回收。\n\n\n\n## 内存模型与回收策略\n\n内存模型:\n\n\"img\"\n\n\n\n可以看到,内存模型分为 :\n\n- Eden\n\n- Survior\n\n- Old\n\n 这3个区,而Survior又分为 From、to两个区\n\n### Eden区\n\nIBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC **更频繁,回收速度也更快**。\n\n> 通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。\n\n### Survivor区\n\nSurvivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。\n\n1、为啥需要Survivor区?\n\n不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。\n\n> 所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。\n\n2、为啥需要俩?\n\n设置两个 Survivor 区最大的好处就是解决内存碎片化。\n\n我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们**只能标记清除**,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。\n\n这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。\n\n### Old 区\n\n老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 --- 整理算法。\n\n除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。\n\n1、大对象\n\n大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。\n\n2、长期存活对象\n\n虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。\n\n3、动态对象年龄\n\n虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。\n\n这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。\n\n\n\n","source":"_posts/Java面试/Java垃圾回收算法.md","raw":"---\ntitle: java垃圾回收算法\ntags:\n - Java面试\ncategories:\n - java\n - java面试\nabbrlink: bdfc8ea1\ndate: 2021-05-16 11:16:42\ndescription: Java垃圾回收原理解析\n---\n\n\n\n# Java垃圾回收算法\n\n[咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/73628158)\n\n## 什么是垃圾(定义)\n\n有两种算法来定义\n\n### 引用计数算法\n\n引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。\n\n> **缺点:**循环引用(其计数永远不会为0)\n\n### 可达性分析算法\n\n可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。\n\n\"image-20210423190611076\"\n\n\n\n> 可达性算法,解决了引用计数所无法解决的问题-“循环依赖”\n\n## 怎么回收(算法)\n\n### 标记-清除算法\n\n\"image-20210424225646255\"\n\n标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;\n\n缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存\n\n\n\n### 复制算法\n\n**回收前:**\n\n\"image-20210424231518421\"\n\n**回收后:**\n\n\"image-20210424231836708\"\n\n\n\n原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:\n\n1. 首先把存活的对象都复制到另一块空内存上\n2. 再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。\n\n优点:解决了标记-清除算法的问题\n\n缺点:同一时间只能使用其中一半的内存\n\n### 标记-整理算法\n\n\"img\"\n\n标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。\n\n优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。\n\n缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。\n\n\n\n### 分代收集算法\n\n分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。\n\n对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。\n\n1. 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。\n2. 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记-整理算法来进行回收。\n\n\n\n## 内存模型与回收策略\n\n内存模型:\n\n\"img\"\n\n\n\n可以看到,内存模型分为 :\n\n- Eden\n\n- Survior\n\n- Old\n\n 这3个区,而Survior又分为 From、to两个区\n\n### Eden区\n\nIBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC **更频繁,回收速度也更快**。\n\n> 通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。\n\n### Survivor区\n\nSurvivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。\n\n1、为啥需要Survivor区?\n\n不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。\n\n> 所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。\n\n2、为啥需要俩?\n\n设置两个 Survivor 区最大的好处就是解决内存碎片化。\n\n我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们**只能标记清除**,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。\n\n这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。\n\n### Old 区\n\n老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 --- 整理算法。\n\n除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。\n\n1、大对象\n\n大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。\n\n2、长期存活对象\n\n虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。\n\n3、动态对象年龄\n\n虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。\n\n这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。\n\n\n\n","slug":"Java面试/Java垃圾回收算法","published":1,"updated":"2021-09-11T08:01:37.796Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6og000xakve4mebdy6p","content":"

Java垃圾回收算法

咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)

\n

什么是垃圾(定义)

有两种算法来定义

\n

引用计数算法

引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。

\n
\n

缺点:循环引用(其计数永远不会为0)

\n
\n

可达性分析算法

可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。

\n

\"image-20210423190611076\"

\n
\n

可达性算法,解决了引用计数所无法解决的问题-“循环依赖”

\n
\n

怎么回收(算法)

标记-清除算法

\"image-20210424225646255\"

\n

标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;

\n

缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存

\n

复制算法

回收前:

\n

\"image-20210424231518421\"

\n

回收后:

\n

\"image-20210424231836708\"

\n

原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:

\n
    \n
  1. 首先把存活的对象都复制到另一块空内存上
  2. \n
  3. 再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。
  4. \n
\n

优点:解决了标记-清除算法的问题

\n

缺点:同一时间只能使用其中一半的内存

\n

标记-整理算法

\"img\"

\n

标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。

\n

优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。

\n

缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。

\n

分代收集算法

分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。

\n

对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

\n
    \n
  1. 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
  2. \n
  3. 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记-整理算法来进行回收。
  4. \n
\n

内存模型与回收策略

内存模型:

\n

\"img\"

\n

可以看到,内存模型分为 :

\n
    \n
  • Eden

    \n
  • \n
  • Survior

    \n
  • \n
  • Old

    \n

    这3个区,而Survior又分为 From、to两个区

    \n
  • \n
\n

Eden区

IBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC 更频繁,回收速度也更快

\n
\n

通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。

\n
\n

Survivor区

Survivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。

\n

1、为啥需要Survivor区?

\n

不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。

\n
\n

所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。

\n
\n

2、为啥需要俩?

\n

设置两个 Survivor 区最大的好处就是解决内存碎片化。

\n

我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们只能标记清除,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。

\n

这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。

\n

Old 区

老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 —- 整理算法。

\n

除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。

\n

1、大对象

\n

大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。

\n

2、长期存活对象

\n

虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。

\n

3、动态对象年龄

\n

虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。

\n

这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

Java垃圾回收算法

咱们从头到尾说一次 Java 垃圾回收 - 知乎 (zhihu.com)

\n

什么是垃圾(定义)

有两种算法来定义

\n

引用计数算法

引用计数算法是通过在对象头中分配一个空间来存储该对象被引用的次数,如果该对象被其他对象引用,则它的引用计数+1,删除一个该对象的引用,则其引用计数-1,当其引用计数为0时,该对象就是垃圾,会被回收。

\n
\n

缺点:循环引用(其计数永远不会为0)

\n
\n

可达性分析算法

可达性分析算法(Reachability Analysis):基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。

\n

\"image-20210423190611076\"

\n
\n

可达性算法,解决了引用计数所无法解决的问题-“循环依赖”

\n
\n

怎么回收(算法)

标记-清除算法

\"image-20210424225646255\"

\n

标记清除算法原理就是,把要回收的内存进行标记,再清除掉这些内存中的无用数据;

\n

缺点:会导致大量碎片化的内存出现,但开辟内存时需要的是整块内存

\n

复制算法

回收前:

\n

\"image-20210424231518421\"

\n

回收后:

\n

\"image-20210424231836708\"

\n

原理:把整块内存平均分为两部分,分配内存时,只使用其中一部分。而这一部分内存满即将满时,jvm进行垃圾回收:

\n
    \n
  1. 首先把存活的对象都复制到另一块空内存上
  2. \n
  3. 再把剩下的可回收的内存一次性进行垃圾回收,得到一块(占总的1/2)内存区域。
  4. \n
\n

优点:解决了标记-清除算法的问题

\n

缺点:同一时间只能使用其中一半的内存

\n

标记-整理算法

\"img\"

\n

标记整理算法(Mark-Compact)标记过程仍然与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。

\n

优点:标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。

\n

缺点:标记-整理算法对内存变动频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。

\n

分代收集算法

分代收集算法融合了上述3种基础的算法思想,而产生的针对不同情况所采用不同算法的一套组合拳。

\n

对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

\n
    \n
  1. 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
  2. \n
  3. 老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记-整理算法来进行回收。
  4. \n
\n

内存模型与回收策略

内存模型:

\n

\"img\"

\n

可以看到,内存模型分为 :

\n
    \n
  • Eden

    \n
  • \n
  • Survior

    \n
  • \n
  • Old

    \n

    这3个区,而Survior又分为 From、to两个区

    \n
  • \n
\n

Eden区

IBM 公司的专业研究表明,有将近98%的对象是朝生夕死,所以针对这一现状,大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC 更频繁,回收速度也更快

\n
\n

通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。

\n
\n

Survivor区

Survivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。

\n

1、为啥需要Survivor区?

\n

不就是新生代到老年代么,直接 Eden 到 Old 不好了吗,为啥要这么复杂。想想如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次 Minor GC 没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。

\n
\n

所以,Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。

\n
\n

2、为啥需要俩?

\n

设置两个 Survivor 区最大的好处就是解决内存碎片化。

\n

我们先假设一下,Survivor 如果只有一个区域会怎样。Minor GC 执行后,Eden 区被清空了,存活的对象放到了 Survivor 区,而之前 Survivor 区中的对象,可能也有一些是需要被清除的。问题来了,这时候我们怎么清除它们?在这种场景下,我们只能标记清除,而我们知道标记清除最大的问题就是内存碎片,在新生代这种经常会消亡的区域,采用标记清除必然会让内存产生严重的碎片化。因为 Survivor 有2个区域,所以每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。

\n

这种机制最大的好处就是,整个过程中,永远有一个 Survivor space 是空的,另一个非空的 Survivor space 是无碎片的。那么,Survivor 为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果 Survivor 区再细分下去,每一块的空间就会比较小,容易导致 Survivor 区满,两块 Survivor 区可能是经过权衡之后的最佳方案。

\n

Old 区

老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记 —- 整理算法。

\n

除了上述所说,在内存担保机制下,无法安置的对象会直接进到老年代,以下几种情况也会进入老年代。

\n

1、大对象

\n

大对象指需要大量连续内存空间的对象,这部分对象不管是不是“朝生夕死”,都会直接进到老年代。这样做主要是为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。当你的系统有非常多“朝生夕死”的大对象时,得注意了。

\n

2、长期存活对象

\n

虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中没经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。

\n

3、动态对象年龄

\n

虚拟机并不重视要求对象年龄必须到15岁,才会放入老年区,如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一般,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。

\n

这其实有点类似于负载均衡,轮询是负载均衡的一种,保证每台机器都分得同样的请求。看似很均衡,但每台机的硬件不通,健康状况不同,我们还可以基于每台机接受的请求数,或每台机的响应时间等,来调整我们的负载均衡算法。

\n"},{"title":"标签外挂","date":"2021-09-08T12:13:46.000Z","abbrlink":"54667693","description":"Hexo博客使用标签外挂","cover":"https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png","_content":"\n## 标签外挂\n\n### note\n{% tabs Tabs %}\n\n```markdown\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n**预览**\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n```markdown\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n预览\n\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n\n\n```markdown\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n```\n\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n\n\n\n### Tab\n```markdown\n{% tabs Unique name, [index] %}\n\nAny content (support inline tags too).\n\n{% endtabs %}\n\nUnique name : Unique name of tabs block tag without comma.\n Will be used in #id's as prefix for each tab with their index numbers.\n If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.\n Only for current url of post/page must be unique!\n[index] : Index number of active tab.\n If not specified, first tab (1) will be selected.\n If index is -1, no tab will be selected. It's will be something like spoiler.\n Optional parameter.\n[Tab caption] : Caption of current tab.\n If not caption specified, unique name with tab index suffix will be used as caption of tab.\n If not caption specified, but specified icon, caption will empty.\n Optional parameter.\n[@icon] : FontAwesome icon name (full-name, look like 'fas fa-font')\n Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'.\n Optional parameter.\n\n```\n\n**方案一:默认选择第一个tab**\n\n```markdown\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n\n**方案二:默认选择第3个tab**\n\n```markdown\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案三:没有预设值**\n\n```markdown\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案四:自定义每个tab的name和图标icon**\n\n```markdown\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n```\n\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n### folding\n\n```\n{% folding 参数(可选), 标题 %}\n\n内容\n\n{% endfolding %}\n\n```\n\n{%tabs t2 -1%}\n\n\n参数位置可以填写颜色和状态,多个参数用空格隔开。\n\n- 颜色\n\nblue, cyan, green, yellow, red\n\n- 状态\n\n状态填写 open 代表默认打开\n\n\n\n\n```markdown\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n```\n\n\n\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n\n{% endtabs %}","source":"_posts/Hexo/标签外挂.md","raw":"---\ntitle: 标签外挂\ndate: '2021-09-08 20:13:46'\nabbrlink: '54667693'\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: Hexo博客使用标签外挂\ncover: https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png\n---\n\n## 标签外挂\n\n### note\n{% tabs Tabs %}\n\n```markdown\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n**预览**\n{% note simple %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default simple %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary simple %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success simple %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info simple %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning simple %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger simple %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n```markdown\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n```\n\n预览\n\n{% note modern %}\n默认 提示块标籤\n{% endnote %}\n\n{% note default modern %}\ndefault 提示块标籤\n{% endnote %}\n\n{% note primary modern %}\nprimary 提示块标籤\n{% endnote %}\n\n{% note success modern %}\nsuccess 提示块标籤\n{% endnote %}\n\n{% note info modern %}\ninfo 提示块标籤\n{% endnote %}\n\n{% note warning modern %}\nwarning 提示块标籤\n{% endnote %}\n\n{% note danger modern %}\ndanger 提示块标籤\n{% endnote %}\n\n\n\n\n\n\n```markdown\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n```\n\n{% note flat %}\n默认 提示块标签\n{% endnote %}\n\n{% note default flat %}\ndefault 提示块标签\n{% endnote %}\n\n{% note primary flat %}\nprimary 提示块标签\n{% endnote %}\n\n{% note success flat %}\nsuccess 提示块标签\n{% endnote %}\n\n{% note info flat %}\ninfo 提示块标签\n{% endnote %}\n\n{% note warning flat %}\nwarning 提示块标签\n{% endnote %}\n\n{% note danger flat %}\ndanger 提示块标签\n{% endnote %}\n\n\n\n{% endtabs %}\n\n\n\n\n\n\n\n\n\n### Tab\n```markdown\n{% tabs Unique name, [index] %}\n\nAny content (support inline tags too).\n\n{% endtabs %}\n\nUnique name : Unique name of tabs block tag without comma.\n Will be used in #id's as prefix for each tab with their index numbers.\n If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.\n Only for current url of post/page must be unique!\n[index] : Index number of active tab.\n If not specified, first tab (1) will be selected.\n If index is -1, no tab will be selected. It's will be something like spoiler.\n Optional parameter.\n[Tab caption] : Caption of current tab.\n If not caption specified, unique name with tab index suffix will be used as caption of tab.\n If not caption specified, but specified icon, caption will empty.\n Optional parameter.\n[@icon] : FontAwesome icon name (full-name, look like 'fas fa-font')\n Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'.\n Optional parameter.\n\n```\n\n**方案一:默认选择第一个tab**\n\n```markdown\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n\n**方案二:默认选择第3个tab**\n\n```markdown\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test2, 3 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案三:没有预设值**\n\n```markdown\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n```\n\n{% tabs test3, -1 %}\n\n**This is Tab 1.**\n\n\n\n**This is Tab 2.**\n\n\n\n**This is Tab 3.**\n\n{% endtabs %}\n\n**方案四:自定义每个tab的name和图标icon**\n\n```markdown\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n```\n\n{% tabs test4 %}\n\n**tab名字为第一个Tab**\n\n\n\n**只有图标 没有Tab名字**\n\n\n\n**名字+icon**\n\n{% endtabs %}\n\n### folding\n\n```\n{% folding 参数(可选), 标题 %}\n\n内容\n\n{% endfolding %}\n\n```\n\n{%tabs t2 -1%}\n\n\n参数位置可以填写颜色和状态,多个参数用空格隔开。\n\n- 颜色\n\nblue, cyan, green, yellow, red\n\n- 状态\n\n状态填写 open 代表默认打开\n\n\n\n\n```markdown\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n```\n\n\n\n{% folding 查看图片测试 %}\n\n这是内容\n\n{% endfolding %}\n\n{% folding cyan open, 查看默认打开的折叠框 %}\n\n这是一个默认打开的折叠框。\n\n{% endfolding %}\n\n{% folding green, 查看代码测试 %}\n\n{% endfolding %}\n\n{% folding yellow, 查看列表测试 %}\n\n- haha\n- hehe\n\n{% endfolding %}\n\n{% folding red, 查看嵌套测试 %}\n\n{% folding blue, 查看嵌套测试2 %}\n\n{% folding 查看嵌套测试3 %}\n\nhahaha \n\n{% endfolding %}\n\n{% endfolding %}\n\n{% endfolding %}\n\n\n{% endtabs %}","slug":"Hexo/标签外挂","published":1,"updated":"2021-09-16T15:28:42.688Z","_id":"cktk8o6oi0011akve2ujw438x","comments":1,"layout":"post","photos":[],"link":"","content":"

标签外挂

\n

note

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note simple %}
默认 提示块标籤
{% endnote %}

{% note default simple %}
default 提示块标籤
{% endnote %}

{% note primary simple %}
primary 提示块标籤
{% endnote %}

{% note success simple %}
success 提示块标籤
{% endnote %}

{% note info simple %}
info 提示块标籤
{% endnote %}

{% note warning simple %}
warning 提示块标籤
{% endnote %}

{% note danger simple %}
danger 提示块标籤
{% endnote %}

\n

预览

\n

默认 提示块标籤

\n
\n

default 提示块标籤

\n
\n

primary 提示块标籤

\n
\n

success 提示块标籤

\n
\n

info 提示块标籤

\n
\n

warning 提示块标籤

\n
\n

danger 提示块标籤

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note modern %}
默认 提示块标籤
{% endnote %}

{% note default modern %}
default 提示块标籤
{% endnote %}

{% note primary modern %}
primary 提示块标籤
{% endnote %}

{% note success modern %}
success 提示块标籤
{% endnote %}

{% note info modern %}
info 提示块标籤
{% endnote %}

{% note warning modern %}
warning 提示块标籤
{% endnote %}

{% note danger modern %}
danger 提示块标籤
{% endnote %}

\n

预览

\n

默认 提示块标籤

\n
\n

default 提示块标籤

\n
\n

primary 提示块标籤

\n
\n

success 提示块标籤

\n
\n

info 提示块标籤

\n
\n

warning 提示块标籤

\n
\n

danger 提示块标籤

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note flat %}
默认 提示块标签
{% endnote %}

{% note default flat %}
default 提示块标签
{% endnote %}

{% note primary flat %}
primary 提示块标签
{% endnote %}

{% note success flat %}
success 提示块标签
{% endnote %}

{% note info flat %}
info 提示块标签
{% endnote %}

{% note warning flat %}
warning 提示块标签
{% endnote %}

{% note danger flat %}
danger 提示块标签
{% endnote %}

\n

默认 提示块标签

\n
\n

default 提示块标签

\n
\n

primary 提示块标签

\n
\n

success 提示块标签

\n
\n

info 提示块标签

\n
\n

warning 提示块标签

\n
\n

danger 提示块标签

\n
\n

Tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{% tabs Unique name, [index] %}
<!-- tab [Tab caption] [@icon] -->
Any content (support inline tags too).
<!-- endtab -->
{% endtabs %}

Unique name : Unique name of tabs block tag without comma.
Will be used in #id's as prefix for each tab with their index numbers.
If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.
Only for current url of post/page must be unique!
[index] : Index number of active tab.
If not specified, first tab (1) will be selected.
If index is -1, no tab will be selected. It's will be something like spoiler.
Optional parameter.
[Tab caption] : Caption of current tab.
If not caption specified, unique name with tab index suffix will be used as caption of tab.
If not caption specified, but specified icon, caption will empty.
Optional parameter.
[@icon] : FontAwesome icon name (full-name, look like 'fas fa-font')
Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'.
Optional parameter.

\n

方案一:默认选择第一个tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test1 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案二:默认选择第3个tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test2, 3 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案三:没有预设值

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test3, -1 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案四:自定义每个tab的name和图标icon

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test4 %}
<!-- tab 第一个Tab -->
**tab名字为第一个Tab**
<!-- endtab -->

<!-- tab @fab fa-apple-pay -->
**只有图标 没有Tab名字**
<!-- endtab -->

<!-- tab 炸弹@fas fa-bomb -->
**名字+icon**
<!-- endtab -->
{% endtabs %}

\n

tab名字为第一个Tab

只有图标 没有Tab名字

名字+icon

\n

folding

\n
1
2
3
4
5
6
{% folding 参数(可选), 标题 %}

内容

{% endfolding %}

\n

参数位置可以填写颜色和状态,多个参数用空格隔开。

\n
    \n
  • 颜色
  • \n
\n

blue, cyan, green, yellow, red

\n
    \n
  • 状态
  • \n
\n

状态填写 open 代表默认打开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{% folding 查看图片测试 %}

这是内容

{% endfolding %}

{% folding cyan open, 查看默认打开的折叠框 %}

这是一个默认打开的折叠框。

{% endfolding %}

{% folding green, 查看代码测试 %}

{% endfolding %}

{% folding yellow, 查看列表测试 %}

- haha
- hehe

{% endfolding %}

{% folding red, 查看嵌套测试 %}

{% folding blue, 查看嵌套测试2 %}

{% folding 查看嵌套测试3 %}

hahaha <span><img src='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/emoji/tieba/%E6%BB%91%E7%A8%BD.png' style='height:24px'></span>

{% endfolding %}

{% endfolding %}

{% endfolding %}

查看图片测试 \n
\n

这是内容

\n
\n
\n
查看默认打开的折叠框 \n
\n

这是一个默认打开的折叠框。

\n
\n
\n
查看代码测试 \n
\n
          </div>\n        </details>\n
\n
查看列表测试 \n
\n
  • haha
  • hehe
\n
\n
\n
查看嵌套测试 \n
\n
查看嵌套测试2
查看嵌套测试3

hahaha

\n
\n
","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

标签外挂

\n

note

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note simple %}
默认 提示块标籤
{% endnote %}

{% note default simple %}
default 提示块标籤
{% endnote %}

{% note primary simple %}
primary 提示块标籤
{% endnote %}

{% note success simple %}
success 提示块标籤
{% endnote %}

{% note info simple %}
info 提示块标籤
{% endnote %}

{% note warning simple %}
warning 提示块标籤
{% endnote %}

{% note danger simple %}
danger 提示块标籤
{% endnote %}

\n

预览

\n

默认 提示块标籤

\n
\n

default 提示块标籤

\n
\n

primary 提示块标籤

\n
\n

success 提示块标籤

\n
\n

info 提示块标籤

\n
\n

warning 提示块标籤

\n
\n

danger 提示块标籤

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note modern %}
默认 提示块标籤
{% endnote %}

{% note default modern %}
default 提示块标籤
{% endnote %}

{% note primary modern %}
primary 提示块标籤
{% endnote %}

{% note success modern %}
success 提示块标籤
{% endnote %}

{% note info modern %}
info 提示块标籤
{% endnote %}

{% note warning modern %}
warning 提示块标籤
{% endnote %}

{% note danger modern %}
danger 提示块标籤
{% endnote %}

\n

预览

\n

默认 提示块标籤

\n
\n

default 提示块标籤

\n
\n

primary 提示块标籤

\n
\n

success 提示块标籤

\n
\n

info 提示块标籤

\n
\n

warning 提示块标籤

\n
\n

danger 提示块标籤

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{% note flat %}
默认 提示块标签
{% endnote %}

{% note default flat %}
default 提示块标签
{% endnote %}

{% note primary flat %}
primary 提示块标签
{% endnote %}

{% note success flat %}
success 提示块标签
{% endnote %}

{% note info flat %}
info 提示块标签
{% endnote %}

{% note warning flat %}
warning 提示块标签
{% endnote %}

{% note danger flat %}
danger 提示块标签
{% endnote %}

\n

默认 提示块标签

\n
\n

default 提示块标签

\n
\n

primary 提示块标签

\n
\n

success 提示块标签

\n
\n

info 提示块标签

\n
\n

warning 提示块标签

\n
\n

danger 提示块标签

\n
\n

Tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{% tabs Unique name, [index] %}
<!-- tab [Tab caption] [@icon] -->
Any content (support inline tags too).
<!-- endtab -->
{% endtabs %}

Unique name : Unique name of tabs block tag without comma.
Will be used in #id's as prefix for each tab with their index numbers.
If there are whitespaces in name, for generate #id all whitespaces will replaced by dashes.
Only for current url of post/page must be unique!
[index] : Index number of active tab.
If not specified, first tab (1) will be selected.
If index is -1, no tab will be selected. It's will be something like spoiler.
Optional parameter.
[Tab caption] : Caption of current tab.
If not caption specified, unique name with tab index suffix will be used as caption of tab.
If not caption specified, but specified icon, caption will empty.
Optional parameter.
[@icon] : FontAwesome icon name (full-name, look like 'fas fa-font')
Can be specified with or without space; e.g. 'Tab caption @icon' similar to 'Tab caption@icon'.
Optional parameter.

\n

方案一:默认选择第一个tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test1 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案二:默认选择第3个tab

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test2, 3 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案三:没有预设值

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test3, -1 %}
<!-- tab -->
**This is Tab 1.**
<!-- endtab -->

<!-- tab -->
**This is Tab 2.**
<!-- endtab -->

<!-- tab -->
**This is Tab 3.**
<!-- endtab -->
{% endtabs %}

\n

This is Tab 1.

This is Tab 2.

This is Tab 3.

\n

方案四:自定义每个tab的name和图标icon

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% tabs test4 %}
<!-- tab 第一个Tab -->
**tab名字为第一个Tab**
<!-- endtab -->

<!-- tab @fab fa-apple-pay -->
**只有图标 没有Tab名字**
<!-- endtab -->

<!-- tab 炸弹@fas fa-bomb -->
**名字+icon**
<!-- endtab -->
{% endtabs %}

\n

tab名字为第一个Tab

只有图标 没有Tab名字

名字+icon

\n

folding

\n
1
2
3
4
5
6
{% folding 参数(可选), 标题 %}

内容

{% endfolding %}

\n

参数位置可以填写颜色和状态,多个参数用空格隔开。

\n
    \n
  • 颜色
  • \n
\n

blue, cyan, green, yellow, red

\n
    \n
  • 状态
  • \n
\n

状态填写 open 代表默认打开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{% folding 查看图片测试 %}

这是内容

{% endfolding %}

{% folding cyan open, 查看默认打开的折叠框 %}

这是一个默认打开的折叠框。

{% endfolding %}

{% folding green, 查看代码测试 %}

{% endfolding %}

{% folding yellow, 查看列表测试 %}

- haha
- hehe

{% endfolding %}

{% folding red, 查看嵌套测试 %}

{% folding blue, 查看嵌套测试2 %}

{% folding 查看嵌套测试3 %}

hahaha <span><img src='https://cdn.jsdelivr.net/gh/xaoxuu/cdn-assets/emoji/tieba/%E6%BB%91%E7%A8%BD.png' style='height:24px'></span>

{% endfolding %}

{% endfolding %}

{% endfolding %}

查看图片测试 \n
\n

这是内容

\n
\n
\n
查看默认打开的折叠框 \n
\n

这是一个默认打开的折叠框。

\n
\n
\n
查看代码测试 \n
\n
          </div>\n        </details>\n
\n
查看列表测试 \n
\n
  • haha
  • hehe
\n
\n
\n
查看嵌套测试 \n
\n
查看嵌套测试2
查看嵌套测试3

hahaha

\n
\n
"},{"title":"1-maven基本概念","abbrlink":"52e2375","date":"2021-04-17T02:51:47.000Z","description":"maven的基本概念、特性介绍及如何使用","_content":"\n\n# Maven基本概念\n\n## Maven学习内容\n\n\"Maven\"\n\n\n\n## Maven简介\n\nMaven主要用于项目构建、依赖管理、项目信息管理\n\n1. 小型项目\n2. 大型企业项目\n3. 传统瀑布式开发\n4. 流行的敏捷开发\n\n\n\n## 项目构建\n\n​\t\t总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编\n\n译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。\n\n\n\n## 项目构建工具\n\n### Ant\n\n最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。\n\n### Maven\n\n- 项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。 \n- 填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。\n- Maven 专注的是依赖管理, 使用 Java 编写。\n\n### Gradle\n\n- 结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。\n- 它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。\n\n\n\n## Maven四大特性\n\n### 依赖管理系统(jar项目的多模块)\n\n- Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。 \n- 在 Java 世界中, 可以用 groupIdartifactIdversion 组成的Coordination( 坐标)唯一标识一个依赖。 \n- 任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。\n\n一个典型的依赖引用如下所示:\n\n```xml\n\n\tjavax.servlet //包名/项目名\n\tjavax.servlet-api //某一模块名\n\t3.1.0\t//所需要jar的版本\n\n```\n\n### 多模块构建\n\n 项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。\n\n### 一致的项目结构(不同的IDE项目结构是一致)\n\n Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。\n\n\n\n\n\n### 一致的构建模型和插件机制\n\n通过 pom 配置 tomcat、 jetty 插件:\n\n```html\n\n\t\n\torg.mortbay.jetty\n\tmaven-jetty-plugin\n\t6.1.25\n\t\n\t10\n\t/test\n\n\n\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/Maven/1-maven基本概念.md","raw":"---\ntitle: 1-maven基本概念\ntags:\n - Maven\ncategories:\n - - java\n - Maven\nabbrlink: '52e2375'\ndate: 2021-04-17 10:51:47\ndescription: maven的基本概念、特性介绍及如何使用\n---\n\n\n# Maven基本概念\n\n## Maven学习内容\n\n\"Maven\"\n\n\n\n## Maven简介\n\nMaven主要用于项目构建、依赖管理、项目信息管理\n\n1. 小型项目\n2. 大型企业项目\n3. 传统瀑布式开发\n4. 流行的敏捷开发\n\n\n\n## 项目构建\n\n​\t\t总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编\n\n译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。\n\n\n\n## 项目构建工具\n\n### Ant\n\n最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。\n\n### Maven\n\n- 项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。 \n- 填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。\n- Maven 专注的是依赖管理, 使用 Java 编写。\n\n### Gradle\n\n- 结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。\n- 它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。\n\n\n\n## Maven四大特性\n\n### 依赖管理系统(jar项目的多模块)\n\n- Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。 \n- 在 Java 世界中, 可以用 groupIdartifactIdversion 组成的Coordination( 坐标)唯一标识一个依赖。 \n- 任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。\n\n一个典型的依赖引用如下所示:\n\n```xml\n\n\tjavax.servlet //包名/项目名\n\tjavax.servlet-api //某一模块名\n\t3.1.0\t//所需要jar的版本\n\n```\n\n### 多模块构建\n\n 项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。\n\n### 一致的项目结构(不同的IDE项目结构是一致)\n\n Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。\n\n\n\n\n\n### 一致的构建模型和插件机制\n\n通过 pom 配置 tomcat、 jetty 插件:\n\n```html\n\n\t\n\torg.mortbay.jetty\n\tmaven-jetty-plugin\n\t6.1.25\n\t\n\t10\n\t/test\n\n\n\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","slug":"Maven/1-maven基本概念","published":1,"updated":"2021-09-10T02:59:29.812Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oj0013akve45f3gq5i","content":"

Maven基本概念

Maven学习内容

\"Maven\"

\n

Maven简介

Maven主要用于项目构建、依赖管理、项目信息管理

\n
    \n
  1. 小型项目
  2. \n
  3. 大型企业项目
  4. \n
  5. 传统瀑布式开发
  6. \n
  7. 流行的敏捷开发
  8. \n
\n

项目构建

​ 总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编

\n

译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。

\n

项目构建工具

Ant

最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。

\n

Maven

    \n
  • 项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。
  • \n
  • 填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。
  • \n
  • Maven 专注的是依赖管理, 使用 Java 编写。
  • \n
\n

Gradle

    \n
  • 结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。
  • \n
  • 它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。
  • \n
\n

Maven四大特性

依赖管理系统(jar项目的多模块)

    \n
  • Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。
  • \n
  • 在 Java 世界中, 可以用 groupIdartifactIdversion 组成的Coordination( 坐标)唯一标识一个依赖。
  • \n
  • 任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。
  • \n
\n

一个典型的依赖引用如下所示:

\n
1
2
3
4
5
<dependency>
\t<groupId>javax.servlet</groupId> //包名/项目名
\t<artifactId>javax.servlet-api</artifactId> //某一模块名
\t<version>3.1.0</version>\t//所需要jar的版本
</dependency>
\n

多模块构建

项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。

\n

一致的项目结构(不同的IDE项目结构是一致)

Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。

\n

一致的构建模型和插件机制

通过 pom 配置 tomcat、 jetty 插件:

\n
1
2
3
4
5
6
7
8
9
10
11
<plugin>
\t<!--这里是所有添加插件配置的地方-->
\t<groupId>org.mortbay.jetty</groupId>
\t<artifactId>maven-jetty-plugin</artifactId>
\t<version>6.1.25</version>
\t<configuration>
\t<scanIntervalSeconds>10</scanIntervalSeconds>
\t<contextPath>/test</contextPath>
</configuration>
</plugin>

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"

Maven基本概念

Maven学习内容

\"Maven\"

\n

Maven简介

Maven主要用于项目构建、依赖管理、项目信息管理

\n
    \n
  1. 小型项目
  2. \n
  3. 大型企业项目
  4. \n
  5. 传统瀑布式开发
  6. \n
  7. 流行的敏捷开发
  8. \n
\n

项目构建

​ 总结一下, 我们会发现, 除了编写源代码, 我们每天有相当一部分时间花在了编

\n

译,运行单元测试, 生成文档, 打包和部署等繁琐且不起眼的工作上, 这就是构建。

\n

项目构建工具

Ant

最早的构建工具, 基于 IDE, 大概是 2000 年有的, 当时是最流行 java 构建工具,不过它的 XML 脚本编写格式让 XML 文件特别大。 对工程构建过程中的过程控制特别好。

\n

Maven

    \n
  • 项目对象模型, 通过其描述信息来管理项目的构建, 报告和文档的软件项目管理工具。
  • \n
  • 填补了 Ant 缺点, Maven 第一次支持了从网络上下载的功能, 仍然采用 xml 作为配置文件格式。
  • \n
  • Maven 专注的是依赖管理, 使用 Java 编写。
  • \n
\n

Gradle

    \n
  • 结合以上两个的优点, 它继承了 Ant 的灵活和 Maven 的生命周期管理, 它最后被 google 作为了 Android 御用管理工具。
  • \n
  • 它最大的区别是不用 XML 作为配置文件格式, 采用了 DSL 格式, 使得脚本更加简洁。
  • \n
\n

Maven四大特性

依赖管理系统(jar项目的多模块)

    \n
  • Maven 为 Java 世界引入了一个新的依赖管理系统 jar 包管理 ,jar 升级时修改配置文件即可。
  • \n
  • 在 Java 世界中, 可以用 groupIdartifactIdversion 组成的Coordination( 坐标)唯一标识一个依赖。
  • \n
  • 任何基于 Maven 构建的项目自身也必须定义这三项属性, 生成的包可以是 Jar 包, 也可以是 war 包或者 jar 包。
  • \n
\n

一个典型的依赖引用如下所示:

\n
1
2
3
4
5
<dependency>
\t<groupId>javax.servlet</groupId> //包名/项目名
\t<artifactId>javax.servlet-api</artifactId> //某一模块名
\t<version>3.1.0</version>\t//所需要jar的版本
</dependency>
\n

多模块构建

项目复查时 dao service controller 层分离将一个项目分解为多个模块已经是很通用的一种方式。在 Maven 中需要定义一个 parent POM 作为一组 module 的聚合 POM。在该 POM 中可以使用 标签来定义一组子模块。 parent POM 不会有什么实际构建产出。 而 parent POM 中的 build 配置以及依赖配置都会自动继承给子 module。

\n

一致的项目结构(不同的IDE项目结构是一致)

Ant 时代大家创建 Java 项目目录时比较随意, 然后通过 Ant 配置指定哪些属于source,那些属于 testSource 等。而 Maven 在设计之初的理念就是 Conversion overconfiguration( 约定大于配置)。 其制定了一套项目目录结构作为标准的 Java 项目结构,解决不同 ide 带来的文件目录不一致问题。

\n

一致的构建模型和插件机制

通过 pom 配置 tomcat、 jetty 插件:

\n
1
2
3
4
5
6
7
8
9
10
11
<plugin>
\t<!--这里是所有添加插件配置的地方-->
\t<groupId>org.mortbay.jetty</groupId>
\t<artifactId>maven-jetty-plugin</artifactId>
\t<version>6.1.25</version>
\t<configuration>
\t<scanIntervalSeconds>10</scanIntervalSeconds>
\t<contextPath>/test</contextPath>
</configuration>
</plugin>

\n"},{"title":"3-maven+IDEA创建webapp","abbrlink":"79c0e989","date":"2021-04-17T11:54:41.000Z","description":"如何用idea编辑器、maven工具构建一个webapp","_content":"\n\n\n\n\n### 创建project\n\n![image-20210502143531044](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-35-33_image-20210502143531044.png)\n\n\n\n![image-20210502143822062](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-38-23_image-20210502143822062.png)\n\n\n\n![image-20210502144127624](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-41-29_image-20210502144127624.png)\n\n接着等待下载一些文件\n\n进入工程下文件 `pom.xml`,做以下配置:\n\n修改Java编译器版本为`1.8`(根据自己情况)\n\n```xml\n\n UTF-8\n 1.8\n 1.8\n\n```\n\n\n\n删除下面两个标签及标签中的内容\n\n```xml\n\n ...\n\n```\n\n如果使用tomcat服务器,在刚才删除的地方添加下面内容\n\n```xml\n\n \n org.apache.tomcat.maven\n tomcat7-maven-plugin\n 2.2 \n \n 8088 \n /test \n UTF-8\n tomcat7\n \n \n\n```\n\n\n\n然后:\n\n![image-20210502145452277](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-54-53_image-20210502145452277.png)\n\n\n\n等待下载完成后\n\n![image-20210502145735190](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-57-36_image-20210502145735190.png)\n\n![image-20210502145859423](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-59-00_image-20210502145859423.png)\n\n![image-20210502150218246](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-02-19_image-20210502150218246.png)\n\n\n\n\n\n配置完成,点击运行\n\n![image-20210502150451149](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-04-52_image-20210502150451149.png)\n\n![image-20210502150620073](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-06-21_image-20210502150620073.png)\n\n浏览器访问结果:\n\n![image-20210502150700084](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-07-01_image-20210502150700084.png)\n\n\n\n\n\n\n\n\n> 下载的插件在repository的org目录下\n>\n> ```\n> D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat\n> ```","source":"_posts/Maven/3-maven+IDEA创建webapp.md","raw":"---\ntitle: 3-maven+IDEA创建webapp\ntags:\n - Maven\ncategories:\n - - java\n - Maven\nabbrlink: 79c0e989\ndate: 2021-04-17 19:54:41\ndescription: 如何用idea编辑器、maven工具构建一个webapp\n---\n\n\n\n\n\n### 创建project\n\n![image-20210502143531044](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-35-33_image-20210502143531044.png)\n\n\n\n![image-20210502143822062](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-38-23_image-20210502143822062.png)\n\n\n\n![image-20210502144127624](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-41-29_image-20210502144127624.png)\n\n接着等待下载一些文件\n\n进入工程下文件 `pom.xml`,做以下配置:\n\n修改Java编译器版本为`1.8`(根据自己情况)\n\n```xml\n\n UTF-8\n 1.8\n 1.8\n\n```\n\n\n\n删除下面两个标签及标签中的内容\n\n```xml\n\n ...\n\n```\n\n如果使用tomcat服务器,在刚才删除的地方添加下面内容\n\n```xml\n\n \n org.apache.tomcat.maven\n tomcat7-maven-plugin\n 2.2 \n \n 8088 \n /test \n UTF-8\n tomcat7\n \n \n\n```\n\n\n\n然后:\n\n![image-20210502145452277](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-54-53_image-20210502145452277.png)\n\n\n\n等待下载完成后\n\n![image-20210502145735190](https://gitee.com/aJream/images/raw/master/img/2021-05-0214-57-36_image-20210502145735190.png)\n\n![image-20210502145859423](https://gitee.com/ajream/images/raw/master/img/2021-05-0214-59-00_image-20210502145859423.png)\n\n![image-20210502150218246](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-02-19_image-20210502150218246.png)\n\n\n\n\n\n配置完成,点击运行\n\n![image-20210502150451149](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-04-52_image-20210502150451149.png)\n\n![image-20210502150620073](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-06-21_image-20210502150620073.png)\n\n浏览器访问结果:\n\n![image-20210502150700084](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-07-01_image-20210502150700084.png)\n\n\n\n\n\n\n\n\n> 下载的插件在repository的org目录下\n>\n> ```\n> D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat\n> ```","slug":"Maven/3-maven+IDEA创建webapp","published":1,"updated":"2021-09-10T03:00:13.294Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ok0018akve39f5g6yx","content":"

创建project

\"image-20210502143531044\"

\n

\"image-20210502143822062\"

\n

\"image-20210502144127624\"

\n

接着等待下载一些文件

\n

进入工程下文件 pom.xml,做以下配置:

\n

修改Java编译器版本为1.8(根据自己情况)

\n
1
2
3
4
5
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
\n

删除下面两个标签及标签中的内容

\n
1
2
3
<pluginManagement>
...
</pluginManagement>
\n

如果使用tomcat服务器,在刚才删除的地方添加下面内容

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version> <!--tomcat版本-->
<configuration>
<port>8088</port> <!--端口-->
<path>/test</path> <!--项目站点名-->
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
\n

然后:

\n

\"image-20210502145452277\"

\n

等待下载完成后

\n

\"image-20210502145735190\"

\n

\"image-20210502145859423\"

\n

\"image-20210502150218246\"

\n

配置完成,点击运行

\n

\"image-20210502150451149\"

\n

\"image-20210502150620073\"

\n

浏览器访问结果:

\n

\"image-20210502150700084\"

\n
\n

下载的插件在repository的org目录下

\n
1
D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

创建project

\"image-20210502143531044\"

\n

\"image-20210502143822062\"

\n

\"image-20210502144127624\"

\n

接着等待下载一些文件

\n

进入工程下文件 pom.xml,做以下配置:

\n

修改Java编译器版本为1.8(根据自己情况)

\n
1
2
3
4
5
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
\n

删除下面两个标签及标签中的内容

\n
1
2
3
<pluginManagement>
...
</pluginManagement>
\n

如果使用tomcat服务器,在刚才删除的地方添加下面内容

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version> <!--tomcat版本-->
<configuration>
<port>8088</port> <!--端口-->
<path>/test</path> <!--项目站点名-->
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
\n

然后:

\n

\"image-20210502145452277\"

\n

等待下载完成后

\n

\"image-20210502145735190\"

\n

\"image-20210502145859423\"

\n

\"image-20210502150218246\"

\n

配置完成,点击运行

\n

\"image-20210502150451149\"

\n

\"image-20210502150620073\"

\n

浏览器访问结果:

\n

\"image-20210502150700084\"

\n
\n

下载的插件在repository的org目录下

\n
1
D:\\maven\\apache-maven-3.8.1\\repository\\org\\apache\\tomcat
\n"},{"title":"MySql(2)-MySQL基础","abbrlink":"39aef30f","date":"2021-06-05T04:18:30.000Z","description":"MySQL介绍与SQL语言使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n\n\n# MySQL基础\n\n\n\n## mysql语法\n\n注释:\n\n- 单行注释:`--这是注释`\n\n- 多行注释:\n\n ```mysql\n /*\n 这是注释\n 这是注释\n 这是注释\n 这是注释\n */\n ```\n\n\nmysql语句不区分大小写,建议小写\n\n## 数据库基本操作\n\n操作数据库->操作数据库的表->操作数据库的表中的数据\n\n\n\n连接数据库\n\n```shell\nmysql -uroot -p密码 #-p和密码之间没有空格\n```\n\n\n\n查看全部数据库\n\n```mysql\nshow databases;\n```\n\n使用某个数据库(假设有个数据库为:school)\n\n```mysql\nuse school;\n```\n\n> 如果名称是sql中的一些关键字,要加反引号\n>\n> ```mysql\n> use `user`;\n> ```\n\n查看该数据库所有的表\n\n```mysql\nshow tables;\n```\n\n查看某个表信息(假设在该数据库中有个表为:student):\n\n```mysql\ndescribe student;\n或\ndesc student;\n```\n\n创建数据库school; (中括号 `[]` 表示可选)\n\n```mysql\ncreate database [if not exist] school;\n```\n\n删除数据库:\n\n```mysql\ndrop database [if exist] school;\n```\n\n\n\n## 数据库的(列)数据类型\n\n### 数值\n\n1. tinyint: 1个字节(-127~128)\n2. smallint: 2个字节\n3. mediumint: 3个字节\n4. **int:4个字节**\n5. bigint:8个字节\n6. float:4个字节\n7. double:8个字节\n8. decimal:字符串形式的浮点数(金融计算常用)\n\n### 字符串\n\n1. char:1个字节(0~255)\n2. **varchar:可变字符串 0~65535**\n3. tinytext: 微型文本 2^8-1\n4. **text:文本串 2^16-1**\n\n\n\n\n\n### 时间日期\n\n1. date\t\tYYYY-MM-DD, 日期格式\n2. time HH:mm:ss 时间格式\n3. **datetime** YYYY-MM-DD HH:mm:ss (最常用)\n4. **timestamp** 1970.1.1到现在的毫秒数(较常用)\n\n5. year:年份\n\n\n\n### NULL\n\n没有值,未知\n\n注意,不要使用NULL进行运算,结果为NULL\n\n\n\n## 字段属性(重要)\n\n每一列(称为字段)都有多个属性,用于描述该列的特征\n\n- unsigned\n\n 无符号整数,表示该列不能为负数\n\n\n\n- zerofill\n - 0填充的\n - 不足的位数用0填充,如:int(3); --输入数字5,自动转为 `005`\n\n\n\n- 自增 auto_increment\n\n 自动在一条记录的基础上(默认) `+1`\n\n 必须是整数类型\n\n\n\n\n\n- 非空 NOT NULL\n - 假设为 not null: 如果不给他赋值,会报错\n\n\n\n例子:创建一个表\n\n```mysql\nCREATE TABLE\nIF\n\tNOT EXISTS `student` (\n\t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号',\n\t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',\n\t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码',\n\t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别',\n\t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄',\n\tPRIMARY KEY ( `id` ) -- 主键\n\t\n\t) ENGINE = INNODB DEFAULT CHARSET = utf8;\n```\n\n\n\n创建表格式( `[]`里面的内容表示可有可无 ):\n\n```mysql\nCREATE TABLE [IF NOT EXISTS] `表名`(\n\t`字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n ...\n `字段名` 列类型 [属性] [索引] [注释]\n)[表类型] [字符集设置] [注释]\n```\n\n\n\n在创建完一个数据库或者表后,可以用以下语句:\n\n```mysql\nSHOW CREATE DATABASE school; -- 查看创建数据库的语句\nSHOW CREATE TABLE student; \t-- 查看创建表的语句\nDESC student; -- 显示表的结构\n```\n\n\n\n\n\n## 数据库引擎\n\n1. INNODB(默认使用)\n2. MYISAM\n\n\n\n| | MYISAM | INNODB |\n| ------------ | ------ | ------------------- |\n| 事务支持 | 不支持 | 支持 |\n| 数据行锁定 | 不支持 | 支持 |\n| 外键约束 | 不支持 | 支持 |\n| 全文索引 | 支持 | 不支持 |\n| 表的空间大小 | 较小 | 较大,为MYISAM的2倍 |\n\n事务\n\n- 是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以\n\n\n\n占用空间:\n\n- InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;\n\n- 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。\n\n\n\n常用操作:\n\n- MYISAM: 节约空间,速度快\n- INNODB:安全性高,事务处理,多表多用户操作\n\n\n\n物理空间存在位置:\n\n所有数据文件都在data目录下,本质还是文件存储\n\n- MYISAM对应文件:\n - *.frm 表结构定义\n - *.MYD 数据文件\n - *.MYI 索引文件\n- INNODB:只有 *.frm 文件,以及上级目录下ibdata1文件\n\n\n\n## 设置字符集编码\n\n```MySQL\nCHARSET=utf8\n```\n\nMySQL默认字符集 Latin1, 不支持中文\n\n可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上\n\n\n\n\n\n## 修改、删除表\n\n### 修改表的结构:ALTER TABLE\n\n修改表名\n\n```MYSQL\nALTER TABLE teacher RENAME AS teacher1;\n```\n\n增加表的字段\n\n```mysql\nALTER TABLE teacher1 ADD age INT(11)\n```\n\n修改表的字段(字段名,字段类型)\n\n```mysql\nALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改l\nALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名\n\n```\n\n删除表的字段\n\n```mysql\nALTER TABLE teacher1 DROP age1; \n```\n\n\n\n### 删除表 DROP TABLE\n\n```mysql\ndrop table [if exists] teacher; -- 一般加上判断,避免出错\n```\n\n\n\n\n\n","source":"_posts/MySQL/2-MySQL基础.md","raw":"---\ntitle: MySql(2)-MySQL基础\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 39aef30f\ndate: 2021-06-05 12:18:30\ndescription: MySQL介绍与SQL语言使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n\n\n# MySQL基础\n\n\n\n## mysql语法\n\n注释:\n\n- 单行注释:`--这是注释`\n\n- 多行注释:\n\n ```mysql\n /*\n 这是注释\n 这是注释\n 这是注释\n 这是注释\n */\n ```\n\n\nmysql语句不区分大小写,建议小写\n\n## 数据库基本操作\n\n操作数据库->操作数据库的表->操作数据库的表中的数据\n\n\n\n连接数据库\n\n```shell\nmysql -uroot -p密码 #-p和密码之间没有空格\n```\n\n\n\n查看全部数据库\n\n```mysql\nshow databases;\n```\n\n使用某个数据库(假设有个数据库为:school)\n\n```mysql\nuse school;\n```\n\n> 如果名称是sql中的一些关键字,要加反引号\n>\n> ```mysql\n> use `user`;\n> ```\n\n查看该数据库所有的表\n\n```mysql\nshow tables;\n```\n\n查看某个表信息(假设在该数据库中有个表为:student):\n\n```mysql\ndescribe student;\n或\ndesc student;\n```\n\n创建数据库school; (中括号 `[]` 表示可选)\n\n```mysql\ncreate database [if not exist] school;\n```\n\n删除数据库:\n\n```mysql\ndrop database [if exist] school;\n```\n\n\n\n## 数据库的(列)数据类型\n\n### 数值\n\n1. tinyint: 1个字节(-127~128)\n2. smallint: 2个字节\n3. mediumint: 3个字节\n4. **int:4个字节**\n5. bigint:8个字节\n6. float:4个字节\n7. double:8个字节\n8. decimal:字符串形式的浮点数(金融计算常用)\n\n### 字符串\n\n1. char:1个字节(0~255)\n2. **varchar:可变字符串 0~65535**\n3. tinytext: 微型文本 2^8-1\n4. **text:文本串 2^16-1**\n\n\n\n\n\n### 时间日期\n\n1. date\t\tYYYY-MM-DD, 日期格式\n2. time HH:mm:ss 时间格式\n3. **datetime** YYYY-MM-DD HH:mm:ss (最常用)\n4. **timestamp** 1970.1.1到现在的毫秒数(较常用)\n\n5. year:年份\n\n\n\n### NULL\n\n没有值,未知\n\n注意,不要使用NULL进行运算,结果为NULL\n\n\n\n## 字段属性(重要)\n\n每一列(称为字段)都有多个属性,用于描述该列的特征\n\n- unsigned\n\n 无符号整数,表示该列不能为负数\n\n\n\n- zerofill\n - 0填充的\n - 不足的位数用0填充,如:int(3); --输入数字5,自动转为 `005`\n\n\n\n- 自增 auto_increment\n\n 自动在一条记录的基础上(默认) `+1`\n\n 必须是整数类型\n\n\n\n\n\n- 非空 NOT NULL\n - 假设为 not null: 如果不给他赋值,会报错\n\n\n\n例子:创建一个表\n\n```mysql\nCREATE TABLE\nIF\n\tNOT EXISTS `student` (\n\t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号',\n\t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',\n\t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码',\n\t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别',\n\t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄',\n\tPRIMARY KEY ( `id` ) -- 主键\n\t\n\t) ENGINE = INNODB DEFAULT CHARSET = utf8;\n```\n\n\n\n创建表格式( `[]`里面的内容表示可有可无 ):\n\n```mysql\nCREATE TABLE [IF NOT EXISTS] `表名`(\n\t`字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n `字段名` 列类型 [属性] [索引] [注释],\n ...\n `字段名` 列类型 [属性] [索引] [注释]\n)[表类型] [字符集设置] [注释]\n```\n\n\n\n在创建完一个数据库或者表后,可以用以下语句:\n\n```mysql\nSHOW CREATE DATABASE school; -- 查看创建数据库的语句\nSHOW CREATE TABLE student; \t-- 查看创建表的语句\nDESC student; -- 显示表的结构\n```\n\n\n\n\n\n## 数据库引擎\n\n1. INNODB(默认使用)\n2. MYISAM\n\n\n\n| | MYISAM | INNODB |\n| ------------ | ------ | ------------------- |\n| 事务支持 | 不支持 | 支持 |\n| 数据行锁定 | 不支持 | 支持 |\n| 外键约束 | 不支持 | 支持 |\n| 全文索引 | 支持 | 不支持 |\n| 表的空间大小 | 较小 | 较大,为MYISAM的2倍 |\n\n事务\n\n- 是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以\n\n\n\n占用空间:\n\n- InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;\n\n- 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。\n\n\n\n常用操作:\n\n- MYISAM: 节约空间,速度快\n- INNODB:安全性高,事务处理,多表多用户操作\n\n\n\n物理空间存在位置:\n\n所有数据文件都在data目录下,本质还是文件存储\n\n- MYISAM对应文件:\n - *.frm 表结构定义\n - *.MYD 数据文件\n - *.MYI 索引文件\n- INNODB:只有 *.frm 文件,以及上级目录下ibdata1文件\n\n\n\n## 设置字符集编码\n\n```MySQL\nCHARSET=utf8\n```\n\nMySQL默认字符集 Latin1, 不支持中文\n\n可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上\n\n\n\n\n\n## 修改、删除表\n\n### 修改表的结构:ALTER TABLE\n\n修改表名\n\n```MYSQL\nALTER TABLE teacher RENAME AS teacher1;\n```\n\n增加表的字段\n\n```mysql\nALTER TABLE teacher1 ADD age INT(11)\n```\n\n修改表的字段(字段名,字段类型)\n\n```mysql\nALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改l\nALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名\n\n```\n\n删除表的字段\n\n```mysql\nALTER TABLE teacher1 DROP age1; \n```\n\n\n\n### 删除表 DROP TABLE\n\n```mysql\ndrop table [if exists] teacher; -- 一般加上判断,避免出错\n```\n\n\n\n\n\n","slug":"MySQL/2-MySQL基础","published":1,"updated":"2021-09-08T13:06:19.369Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ol001bakve1lqxghp8","content":"

MySQL基础

mysql语法

注释:

\n
    \n
  • 单行注释:--这是注释

    \n
  • \n
  • 多行注释:

    \n
    1
    2
    3
    4
    5
    6
    /*
    这是注释
    这是注释
    这是注释
    这是注释
    */
    \n
  • \n
\n

mysql语句不区分大小写,建议小写

\n

数据库基本操作

操作数据库->操作数据库的表->操作数据库的表中的数据

\n

连接数据库

\n
1
mysql -uroot -p密码          #-p和密码之间没有空格
\n

查看全部数据库

\n
1
show databases;
\n

使用某个数据库(假设有个数据库为:school)

\n
1
use school;
\n
\n

如果名称是sql中的一些关键字,要加反引号

\n
1
use `user`;
\n
\n

查看该数据库所有的表

\n
1
show tables;
\n

查看某个表信息(假设在该数据库中有个表为:student):

\n
1
2
3
describe student;

desc student;
\n

创建数据库school; (中括号 [] 表示可选)

\n
1
create database [if not exist] school;
\n

删除数据库:

\n
1
drop database [if exist] school;
\n

数据库的(列)数据类型

数值

    \n
  1. tinyint: 1个字节(-127~128)
  2. \n
  3. smallint: 2个字节
  4. \n
  5. mediumint: 3个字节
  6. \n
  7. int:4个字节
  8. \n
  9. bigint:8个字节
  10. \n
  11. float:4个字节
  12. \n
  13. double:8个字节
  14. \n
  15. decimal:字符串形式的浮点数(金融计算常用)
  16. \n
\n

字符串

    \n
  1. char:1个字节(0~255)
  2. \n
  3. varchar:可变字符串 0~65535
  4. \n
  5. tinytext: 微型文本 2^8-1
  6. \n
  7. text:文本串 2^16-1
  8. \n
\n

时间日期

    \n
  1. date YYYY-MM-DD, 日期格式
  2. \n
  3. time HH:mm:ss 时间格式
  4. \n
  5. datetime YYYY-MM-DD HH:mm:ss (最常用)
  6. \n
  7. timestamp 1970.1.1到现在的毫秒数(较常用)

    \n
  8. \n
  9. year:年份

    \n
  10. \n
\n

NULL

没有值,未知

\n

注意,不要使用NULL进行运算,结果为NULL

\n

字段属性(重要)

每一列(称为字段)都有多个属性,用于描述该列的特征

\n
    \n
  • unsigned

    \n

    无符号整数,表示该列不能为负数

    \n
  • \n
\n
    \n
  • zerofill
      \n
    • 0填充的
    • \n
    • 不足的位数用0填充,如:int(3); —输入数字5,自动转为 005
    • \n
    \n
  • \n
\n
    \n
  • 自增 auto_increment

    \n

    自动在一条记录的基础上(默认) +1

    \n

    必须是整数类型

    \n
  • \n
\n
    \n
  • 非空 NOT NULL
      \n
    • 假设为 not null: 如果不给他赋值,会报错
    • \n
    \n
  • \n
\n

例子:创建一个表

\n
1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE
IF
\tNOT EXISTS `student` (
\t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号',
\t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',
\t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码',
\t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别',
\t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄',
\tPRIMARY KEY ( `id` ) -- 主键
\t
\t) ENGINE = INNODB DEFAULT CHARSET = utf8;
\n

创建表格式( []里面的内容表示可有可无 ):

\n
1
2
3
4
5
6
7
8
CREATE TABLE [IF NOT EXISTS] `表名`(
\t`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
...
`字段名` 列类型 [属性] [索引] [注释]
)[表类型] [字符集设置] [注释]
\n

在创建完一个数据库或者表后,可以用以下语句:

\n
1
2
3
SHOW CREATE DATABASE school; -- 查看创建数据库的语句
SHOW CREATE TABLE student; \t-- 查看创建表的语句
DESC student; -- 显示表的结构
\n

数据库引擎

    \n
  1. INNODB(默认使用)
  2. \n
  3. MYISAM
  4. \n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
MYISAMINNODB
事务支持不支持支持
数据行锁定不支持支持
外键约束不支持支持
全文索引支持不支持
表的空间大小较小较大,为MYISAM的2倍
\n
\n

事务

\n
    \n
  • 是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以
  • \n
\n

占用空间:

\n
    \n
  • InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;

    \n
  • \n
  • 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。

    \n
  • \n
\n

常用操作:

\n
    \n
  • MYISAM: 节约空间,速度快
  • \n
  • INNODB:安全性高,事务处理,多表多用户操作
  • \n
\n

物理空间存在位置:

\n

所有数据文件都在data目录下,本质还是文件存储

\n
    \n
  • MYISAM对应文件:
      \n
    • *.frm 表结构定义
    • \n
    • *.MYD 数据文件
    • \n
    • *.MYI 索引文件
    • \n
    \n
  • \n
  • INNODB:只有 *.frm 文件,以及上级目录下ibdata1文件
  • \n
\n

设置字符集编码

1
CHARSET=utf8
\n

MySQL默认字符集 Latin1, 不支持中文

\n

可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上

\n

修改、删除表

修改表的结构:ALTER TABLE

修改表名

\n
1
ALTER TABLE teacher RENAME AS teacher1;
\n

增加表的字段

\n
1
ALTER TABLE teacher1 ADD age INT(11)
\n

修改表的字段(字段名,字段类型)

\n
1
2
3
ALTER TABLE teacher1 MODIFY age VARCHAR(11)  -- 修改l
ALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名

\n

删除表的字段

\n
1
ALTER TABLE teacher1 DROP age1; 
\n

删除表 DROP TABLE

1
drop table [if exists] teacher;   -- 一般加上判断,避免出错
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

MySQL基础

mysql语法

注释:

\n
    \n
  • 单行注释:--这是注释

    \n
  • \n
  • 多行注释:

    \n
    1
    2
    3
    4
    5
    6
    /*
    这是注释
    这是注释
    这是注释
    这是注释
    */
    \n
  • \n
\n

mysql语句不区分大小写,建议小写

\n

数据库基本操作

操作数据库->操作数据库的表->操作数据库的表中的数据

\n

连接数据库

\n
1
mysql -uroot -p密码          #-p和密码之间没有空格
\n

查看全部数据库

\n
1
show databases;
\n

使用某个数据库(假设有个数据库为:school)

\n
1
use school;
\n
\n

如果名称是sql中的一些关键字,要加反引号

\n
1
use `user`;
\n
\n

查看该数据库所有的表

\n
1
show tables;
\n

查看某个表信息(假设在该数据库中有个表为:student):

\n
1
2
3
describe student;

desc student;
\n

创建数据库school; (中括号 [] 表示可选)

\n
1
create database [if not exist] school;
\n

删除数据库:

\n
1
drop database [if exist] school;
\n

数据库的(列)数据类型

数值

    \n
  1. tinyint: 1个字节(-127~128)
  2. \n
  3. smallint: 2个字节
  4. \n
  5. mediumint: 3个字节
  6. \n
  7. int:4个字节
  8. \n
  9. bigint:8个字节
  10. \n
  11. float:4个字节
  12. \n
  13. double:8个字节
  14. \n
  15. decimal:字符串形式的浮点数(金融计算常用)
  16. \n
\n

字符串

    \n
  1. char:1个字节(0~255)
  2. \n
  3. varchar:可变字符串 0~65535
  4. \n
  5. tinytext: 微型文本 2^8-1
  6. \n
  7. text:文本串 2^16-1
  8. \n
\n

时间日期

    \n
  1. date YYYY-MM-DD, 日期格式
  2. \n
  3. time HH:mm:ss 时间格式
  4. \n
  5. datetime YYYY-MM-DD HH:mm:ss (最常用)
  6. \n
  7. timestamp 1970.1.1到现在的毫秒数(较常用)

    \n
  8. \n
  9. year:年份

    \n
  10. \n
\n

NULL

没有值,未知

\n

注意,不要使用NULL进行运算,结果为NULL

\n

字段属性(重要)

每一列(称为字段)都有多个属性,用于描述该列的特征

\n
    \n
  • unsigned

    \n

    无符号整数,表示该列不能为负数

    \n
  • \n
\n
    \n
  • zerofill
      \n
    • 0填充的
    • \n
    • 不足的位数用0填充,如:int(3); —输入数字5,自动转为 005
    • \n
    \n
  • \n
\n
    \n
  • 自增 auto_increment

    \n

    自动在一条记录的基础上(默认) +1

    \n

    必须是整数类型

    \n
  • \n
\n
    \n
  • 非空 NOT NULL
      \n
    • 假设为 not null: 如果不给他赋值,会报错
    • \n
    \n
  • \n
\n

例子:创建一个表

\n
1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE
IF
\tNOT EXISTS `student` (
\t`id` INT ( 4 ) NOT NULL AUTO_INCREMENT COMMENT '学号',
\t`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',
\t`password` VARCHAR ( 30 ) NOT NULL DEFAULT '123456' COMMENT '密码',
\t`sex` VARCHAR ( 2 ) NOT NULL DEFAULT '保密' COMMENT '性别',
\t`age` INT ( 3 ) NOT NULL DEFAULT 18 COMMENT '年龄',
\tPRIMARY KEY ( `id` ) -- 主键
\t
\t) ENGINE = INNODB DEFAULT CHARSET = utf8;
\n

创建表格式( []里面的内容表示可有可无 ):

\n
1
2
3
4
5
6
7
8
CREATE TABLE [IF NOT EXISTS] `表名`(
\t`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
`字段名` 列类型 [属性] [索引] [注释],
...
`字段名` 列类型 [属性] [索引] [注释]
)[表类型] [字符集设置] [注释]
\n

在创建完一个数据库或者表后,可以用以下语句:

\n
1
2
3
SHOW CREATE DATABASE school; -- 查看创建数据库的语句
SHOW CREATE TABLE student; \t-- 查看创建表的语句
DESC student; -- 显示表的结构
\n

数据库引擎

    \n
  1. INNODB(默认使用)
  2. \n
  3. MYISAM
  4. \n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
MYISAMINNODB
事务支持不支持支持
数据行锁定不支持支持
外键约束不支持支持
全文索引支持不支持
表的空间大小较小较大,为MYISAM的2倍
\n
\n

事务

\n
    \n
  • 是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以
  • \n
\n

占用空间:

\n
    \n
  • InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;

    \n
  • \n
  • 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。

    \n
  • \n
\n

常用操作:

\n
    \n
  • MYISAM: 节约空间,速度快
  • \n
  • INNODB:安全性高,事务处理,多表多用户操作
  • \n
\n

物理空间存在位置:

\n

所有数据文件都在data目录下,本质还是文件存储

\n
    \n
  • MYISAM对应文件:
      \n
    • *.frm 表结构定义
    • \n
    • *.MYD 数据文件
    • \n
    • *.MYI 索引文件
    • \n
    \n
  • \n
  • INNODB:只有 *.frm 文件,以及上级目录下ibdata1文件
  • \n
\n

设置字符集编码

1
CHARSET=utf8
\n

MySQL默认字符集 Latin1, 不支持中文

\n

可以在 my.ini文件配置,但发到别人电脑时可能会出错,最好在创建表时也写上

\n

修改、删除表

修改表的结构:ALTER TABLE

修改表名

\n
1
ALTER TABLE teacher RENAME AS teacher1;
\n

增加表的字段

\n
1
ALTER TABLE teacher1 ADD age INT(11)
\n

修改表的字段(字段名,字段类型)

\n
1
2
3
ALTER TABLE teacher1 MODIFY age VARCHAR(11)  -- 修改l
ALTER TABLE teacher1 CHANGE age age1 int(11) -- 字段重命名

\n

删除表的字段

\n
1
ALTER TABLE teacher1 DROP age1; 
\n

删除表 DROP TABLE

1
drop table [if exists] teacher;   -- 一般加上判断,避免出错
\n"},{"title":"MySql(1)-数据库认识","abbrlink":"b9bb0bd5","date":"2021-06-05T02:25:15.000Z","description":"数据库介绍(数据模型、系统结构与组成)","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n# 数据库介绍与认识\n\n\n\n数据库管理系统:DBMS(database manage system)\n\n## 数据模型\n\n- 概念模型:不依赖于任何计算机系统和DBMS的数据模型\n- 逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计\n\n> 组成要素:\n>\n> - 数据结构\n> - 数据操作\n> - 数据完整性的约束条件\n\n\n\n## 概念模型\n\n概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:**实体-联系方法(E-R方法)**\n\n### 实体联系模型\n\n- 实体(Entity):客观世界事物的集合,如学生、教师……\n- 属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号……\n- 码(Key):唯一标识实体的属性集合,如学号\n- 域(Domain):属性的取值范围,如性别有男、女\n\n\n\n> - 实体型\n>\n> - 实体集\n> - 联系\n\n\n\n### 实体联系类型\n\n- 一对一(1:1)\n- 一对多(1:N)\n- 多对多(M:N)\n\n\n\n## 常用数据模型\n\n### 层次模型\n\n用**树形结构**来表示各类实体以及实体间的联系的数据模型\n\n1. 数据结构\n - 有且只有一个结点,无父结点,该结点为根结点\n - 根结点外的其他结点有且仅有一个父结点\n2. 表现形式(以多对多为例)\n - 冗余结点法\n - 虚拟结点法\n3. 数据操作和完整性约束\n - 数据操作:增删查改\n - 完整性约束\n4. 数据存储形式\n - 邻接法\n - 链接法\n5. 优缺点\n - 优点:结构简单,查询效率高\n - 缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发\n\n\n\n### 网状模型\n\n用**网状结构**来表示各类实体之间的联系的数据模型\n\n1. 数据结构\n - 允许一个以上结点无双亲\n - 一个结点可以有多于一个的双亲\n2. 优缺点:\n - 优点:描述方便,存储效率高\n - 缺点:结构复杂,处理复杂\n\n\n\n### 关系模型\n\n用**关系**来表示实体与实体间的联系\n\n1. 数据结构\n\n - 一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系\n - 元组:表的一行称为元组,一个元组可以表示一个实体或实体间的联系\n - 属性:表中的一列称为关系的一个属性\n - 分量:元组中的一个属性值\n\n\"image-20210323234510823\"\n\n2. 数据操作:增删查改\n3. 完整性约束:实体完整性,参照完整性、用户自定义完整性\n\n\n\n## 系统结构\n\n### 二级映像功能\n\n- 模式/内模式映像\n- 外模式/模式映像\n\n\n\n## 数据库组成\n\n1. 数据库\n2. DBMS\n3. 应用程序\n4. 数据库管理员(DBA)\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/MySQL/1-数据库认识.md","raw":"---\ntitle: MySql(1)-数据库认识\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: b9bb0bd5\ndate: 2021-06-05 10:25:15\ndescription: 数据库介绍(数据模型、系统结构与组成)\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n# 数据库介绍与认识\n\n\n\n数据库管理系统:DBMS(database manage system)\n\n## 数据模型\n\n- 概念模型:不依赖于任何计算机系统和DBMS的数据模型\n- 逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计\n\n> 组成要素:\n>\n> - 数据结构\n> - 数据操作\n> - 数据完整性的约束条件\n\n\n\n## 概念模型\n\n概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:**实体-联系方法(E-R方法)**\n\n### 实体联系模型\n\n- 实体(Entity):客观世界事物的集合,如学生、教师……\n- 属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号……\n- 码(Key):唯一标识实体的属性集合,如学号\n- 域(Domain):属性的取值范围,如性别有男、女\n\n\n\n> - 实体型\n>\n> - 实体集\n> - 联系\n\n\n\n### 实体联系类型\n\n- 一对一(1:1)\n- 一对多(1:N)\n- 多对多(M:N)\n\n\n\n## 常用数据模型\n\n### 层次模型\n\n用**树形结构**来表示各类实体以及实体间的联系的数据模型\n\n1. 数据结构\n - 有且只有一个结点,无父结点,该结点为根结点\n - 根结点外的其他结点有且仅有一个父结点\n2. 表现形式(以多对多为例)\n - 冗余结点法\n - 虚拟结点法\n3. 数据操作和完整性约束\n - 数据操作:增删查改\n - 完整性约束\n4. 数据存储形式\n - 邻接法\n - 链接法\n5. 优缺点\n - 优点:结构简单,查询效率高\n - 缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发\n\n\n\n### 网状模型\n\n用**网状结构**来表示各类实体之间的联系的数据模型\n\n1. 数据结构\n - 允许一个以上结点无双亲\n - 一个结点可以有多于一个的双亲\n2. 优缺点:\n - 优点:描述方便,存储效率高\n - 缺点:结构复杂,处理复杂\n\n\n\n### 关系模型\n\n用**关系**来表示实体与实体间的联系\n\n1. 数据结构\n\n - 一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系\n - 元组:表的一行称为元组,一个元组可以表示一个实体或实体间的联系\n - 属性:表中的一列称为关系的一个属性\n - 分量:元组中的一个属性值\n\n\"image-20210323234510823\"\n\n2. 数据操作:增删查改\n3. 完整性约束:实体完整性,参照完整性、用户自定义完整性\n\n\n\n## 系统结构\n\n### 二级映像功能\n\n- 模式/内模式映像\n- 外模式/模式映像\n\n\n\n## 数据库组成\n\n1. 数据库\n2. DBMS\n3. 应用程序\n4. 数据库管理员(DBA)\n\n\n\n\n\n\n\n\n\n\n\n","slug":"MySQL/1-数据库认识","published":1,"updated":"2021-09-10T03:01:10.897Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oo001gakve5kd5ad6i","content":"

数据库介绍与认识

数据库管理系统:DBMS(database manage system)

\n

数据模型

    \n
  • 概念模型:不依赖于任何计算机系统和DBMS的数据模型
  • \n
  • 逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计
  • \n
\n
\n

组成要素:

\n
    \n
  • 数据结构
  • \n
  • 数据操作
  • \n
  • 数据完整性的约束条件
  • \n
\n
\n

概念模型

概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:实体-联系方法(E-R方法)

\n

实体联系模型

    \n
  • 实体(Entity):客观世界事物的集合,如学生、教师……
  • \n
  • 属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号……
  • \n
  • 码(Key):唯一标识实体的属性集合,如学号
  • \n
  • 域(Domain):属性的取值范围,如性别有男、女
  • \n
\n
\n
    \n
  • 实体型

    \n
  • \n
  • 实体集

    \n
  • \n
  • 联系
  • \n
\n
\n

实体联系类型

    \n
  • 一对一(1:1)
  • \n
  • 一对多(1:N)
  • \n
  • 多对多(M:N)
  • \n
\n

常用数据模型

层次模型

树形结构来表示各类实体以及实体间的联系的数据模型

\n
    \n
  1. 数据结构
      \n
    • 有且只有一个结点,无父结点,该结点为根结点
    • \n
    • 根结点外的其他结点有且仅有一个父结点
    • \n
    \n
  2. \n
  3. 表现形式(以多对多为例)
      \n
    • 冗余结点法
    • \n
    • 虚拟结点法
    • \n
    \n
  4. \n
  5. 数据操作和完整性约束
      \n
    • 数据操作:增删查改
    • \n
    • 完整性约束
    • \n
    \n
  6. \n
  7. 数据存储形式
      \n
    • 邻接法
    • \n
    • 链接法
    • \n
    \n
  8. \n
  9. 优缺点
      \n
    • 优点:结构简单,查询效率高
    • \n
    • 缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发
    • \n
    \n
  10. \n
\n

网状模型

网状结构来表示各类实体之间的联系的数据模型

\n
    \n
  1. 数据结构
      \n
    • 允许一个以上结点无双亲
    • \n
    • 一个结点可以有多于一个的双亲
    • \n
    \n
  2. \n
  3. 优缺点:
      \n
    • 优点:描述方便,存储效率高
    • \n
    • 缺点:结构复杂,处理复杂
    • \n
    \n
  4. \n
\n

关系模型

关系来表示实体与实体间的联系

\n
    \n
  1. 数据结构

    \n
      \n
    • 一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系
    • \n
    • 元组:表的一行称为元组,一个元组可以表示一个实体或实体间的联系
    • \n
    • 属性:表中的一列称为关系的一个属性
    • \n
    • 分量:元组中的一个属性值
    • \n
    \n
  2. \n
\n

\"image-20210323234510823\"

\n
    \n
  1. 数据操作:增删查改
  2. \n
  3. 完整性约束:实体完整性,参照完整性、用户自定义完整性
  4. \n
\n

系统结构

二级映像功能

    \n
  • 模式/内模式映像
  • \n
  • 外模式/模式映像
  • \n
\n

数据库组成

    \n
  1. 数据库
  2. \n
  3. DBMS
  4. \n
  5. 应用程序
  6. \n
  7. 数据库管理员(DBA)
  8. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

数据库介绍与认识

数据库管理系统:DBMS(database manage system)

\n

数据模型

    \n
  • 概念模型:不依赖于任何计算机系统和DBMS的数据模型
  • \n
  • 逻辑模型:与具体DBMS相关的模型,用于完成具体数据库设计
  • \n
\n
\n

组成要素:

\n
    \n
  • 数据结构
  • \n
  • 数据操作
  • \n
  • 数据完整性的约束条件
  • \n
\n
\n

概念模型

概念模型是数据库设计人员进行数据库设计的工具,常用概念模型工具是:实体-联系方法(E-R方法)

\n

实体联系模型

    \n
  • 实体(Entity):客观世界事物的集合,如学生、教师……
  • \n
  • 属性(Attribute):一个实体拥有各种属性,如学生有姓名、学号……
  • \n
  • 码(Key):唯一标识实体的属性集合,如学号
  • \n
  • 域(Domain):属性的取值范围,如性别有男、女
  • \n
\n
\n
    \n
  • 实体型

    \n
  • \n
  • 实体集

    \n
  • \n
  • 联系
  • \n
\n
\n

实体联系类型

    \n
  • 一对一(1:1)
  • \n
  • 一对多(1:N)
  • \n
  • 多对多(M:N)
  • \n
\n

常用数据模型

层次模型

树形结构来表示各类实体以及实体间的联系的数据模型

\n
    \n
  1. 数据结构
      \n
    • 有且只有一个结点,无父结点,该结点为根结点
    • \n
    • 根结点外的其他结点有且仅有一个父结点
    • \n
    \n
  2. \n
  3. 表现形式(以多对多为例)
      \n
    • 冗余结点法
    • \n
    • 虚拟结点法
    • \n
    \n
  4. \n
  5. 数据操作和完整性约束
      \n
    • 数据操作:增删查改
    • \n
    • 完整性约束
    • \n
    \n
  6. \n
  7. 数据存储形式
      \n
    • 邻接法
    • \n
    • 链接法
    • \n
    \n
  8. \n
  9. 优缺点
      \n
    • 优点:结构简单,查询效率高
    • \n
    • 缺点:(1)对于非层次结构的数据表示复杂;(2)查询必须从根结点出发
    • \n
    \n
  10. \n
\n

网状模型

网状结构来表示各类实体之间的联系的数据模型

\n
    \n
  1. 数据结构
      \n
    • 允许一个以上结点无双亲
    • \n
    • 一个结点可以有多于一个的双亲
    • \n
    \n
  2. \n
  3. 优缺点:
      \n
    • 优点:描述方便,存储效率高
    • \n
    • 缺点:结构复杂,处理复杂
    • \n
    \n
  4. \n
\n

关系模型

关系来表示实体与实体间的联系

\n
    \n
  1. 数据结构

    \n
      \n
    • 一个关系就是一张二维表,表有表名,表中内容是对应关系模式在某个时刻的值,称为一个关系
    • \n
    • 元组:表的一行称为元组,一个元组可以表示一个实体或实体间的联系
    • \n
    • 属性:表中的一列称为关系的一个属性
    • \n
    • 分量:元组中的一个属性值
    • \n
    \n
  2. \n
\n

\"image-20210323234510823\"

\n
    \n
  1. 数据操作:增删查改
  2. \n
  3. 完整性约束:实体完整性,参照完整性、用户自定义完整性
  4. \n
\n

系统结构

二级映像功能

    \n
  • 模式/内模式映像
  • \n
  • 外模式/模式映像
  • \n
\n

数据库组成

    \n
  1. 数据库
  2. \n
  3. DBMS
  4. \n
  5. 应用程序
  6. \n
  7. 数据库管理员(DBA)
  8. \n
\n"},{"title":"MySql(4)-MySQL事务","abbrlink":"2a3cf99e","date":"2021-06-08T02:51:44.000Z","description":"MySQL事务原则(原子性、一致性、持久性、隔离性)、事务执行","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# 事务\n\n## 什么是事务\n\n什么是事务:要么都成功,要么都失败\n\n> 比如:\n>\n> a 转账给 b 100元\n>\n> b 收到 a 的钱 100元\n>\n> 这两者应该:要么都成功,要么都失败\n\n\n\n事务原则:ACID原则->【原子性、一致性、隔离性、持久性】\n\n- 原子性:要么都成功,要么都失败\n- 一致性:事务前后(时间上)数据完整性一致\n- 持久性:事务一旦提交则不可逆\n- 隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰\n\n\n\n> 隔离导致的一些问题:\n>\n> - 脏读:一个事务读取另一个事务未提交的数据\n> - 不可重复读:在一个事务内读取表中某一数据,多次读取结果不同\n> - 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致\n\n\n\n\n\n## 事务执行\n\nMySQL默认开启事务自动提交\n\n手动处理事务:\n\n```mysql\nset autocommit = 0 -- 关闭事务自动提交\nstart transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内\n\n/**\n * 数据操作\n */\n\n-- 提交:持久化(成功)\ncommit\n\n-- 回滚:回到原来样子(事务失败)\nrollback\n\n-- 事务结束\nset autocommit = 1 -- 恢复自动提交\n\nsavepoint 保存点名 ; -- 设置事务保存点\nrollback to savepoint 保存点名 ; -- 回到事务保存点\nrelease savepoint 保存点名; -- 撤销保存点\n\n\n\n```\n\n\n\n","source":"_posts/MySQL/4-MySQL事务.md","raw":"---\ntitle: MySql(4)-MySQL事务\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 2a3cf99e\ndate: 2021-06-08 10:51:44\ndescription: MySQL事务原则(原子性、一致性、持久性、隔离性)、事务执行\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# 事务\n\n## 什么是事务\n\n什么是事务:要么都成功,要么都失败\n\n> 比如:\n>\n> a 转账给 b 100元\n>\n> b 收到 a 的钱 100元\n>\n> 这两者应该:要么都成功,要么都失败\n\n\n\n事务原则:ACID原则->【原子性、一致性、隔离性、持久性】\n\n- 原子性:要么都成功,要么都失败\n- 一致性:事务前后(时间上)数据完整性一致\n- 持久性:事务一旦提交则不可逆\n- 隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰\n\n\n\n> 隔离导致的一些问题:\n>\n> - 脏读:一个事务读取另一个事务未提交的数据\n> - 不可重复读:在一个事务内读取表中某一数据,多次读取结果不同\n> - 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致\n\n\n\n\n\n## 事务执行\n\nMySQL默认开启事务自动提交\n\n手动处理事务:\n\n```mysql\nset autocommit = 0 -- 关闭事务自动提交\nstart transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内\n\n/**\n * 数据操作\n */\n\n-- 提交:持久化(成功)\ncommit\n\n-- 回滚:回到原来样子(事务失败)\nrollback\n\n-- 事务结束\nset autocommit = 1 -- 恢复自动提交\n\nsavepoint 保存点名 ; -- 设置事务保存点\nrollback to savepoint 保存点名 ; -- 回到事务保存点\nrelease savepoint 保存点名; -- 撤销保存点\n\n\n\n```\n\n\n\n","slug":"MySQL/4-MySQL事务","published":1,"updated":"2021-09-08T13:06:03.208Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oq001jakvebouthuqu","content":"

事务

什么是事务

什么是事务:要么都成功,要么都失败

\n
\n

比如:

\n

a 转账给 b 100元

\n

b 收到 a 的钱 100元

\n

这两者应该:要么都成功,要么都失败

\n
\n

事务原则:ACID原则->【原子性、一致性、隔离性、持久性】

\n
    \n
  • 原子性:要么都成功,要么都失败
  • \n
  • 一致性:事务前后(时间上)数据完整性一致
  • \n
  • 持久性:事务一旦提交则不可逆
  • \n
  • 隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰
  • \n
\n
\n

隔离导致的一些问题:

\n
    \n
  • 脏读:一个事务读取另一个事务未提交的数据
  • \n
  • 不可重复读:在一个事务内读取表中某一数据,多次读取结果不同
  • \n
  • 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致
  • \n
\n
\n

事务执行

MySQL默认开启事务自动提交

\n

手动处理事务:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
set autocommit = 0 -- 关闭事务自动提交
start transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内

/**
* 数据操作
*/

-- 提交:持久化(成功)
commit

-- 回滚:回到原来样子(事务失败)
rollback

-- 事务结束
set autocommit = 1 -- 恢复自动提交

savepoint 保存点名 ; -- 设置事务保存点
rollback to savepoint 保存点名 ; -- 回到事务保存点
release savepoint 保存点名; -- 撤销保存点



\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

事务

什么是事务

什么是事务:要么都成功,要么都失败

\n
\n

比如:

\n

a 转账给 b 100元

\n

b 收到 a 的钱 100元

\n

这两者应该:要么都成功,要么都失败

\n
\n

事务原则:ACID原则->【原子性、一致性、隔离性、持久性】

\n
    \n
  • 原子性:要么都成功,要么都失败
  • \n
  • 一致性:事务前后(时间上)数据完整性一致
  • \n
  • 持久性:事务一旦提交则不可逆
  • \n
  • 隔离性:多个用户操作一个数据库,数据库为每一个用户开启的事务,应该相互隔离,相互之间不能干扰
  • \n
\n
\n

隔离导致的一些问题:

\n
    \n
  • 脏读:一个事务读取另一个事务未提交的数据
  • \n
  • 不可重复读:在一个事务内读取表中某一数据,多次读取结果不同
  • \n
  • 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致
  • \n
\n
\n

事务执行

MySQL默认开启事务自动提交

\n

手动处理事务:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
set autocommit = 0 -- 关闭事务自动提交
start transaction -- 标记一个事务开始,从这句话后的sql都在一个事务内

/**
* 数据操作
*/

-- 提交:持久化(成功)
commit

-- 回滚:回到原来样子(事务失败)
rollback

-- 事务结束
set autocommit = 1 -- 恢复自动提交

savepoint 保存点名 ; -- 设置事务保存点
rollback to savepoint 保存点名 ; -- 回到事务保存点
release savepoint 保存点名; -- 撤销保存点



\n"},{"title":"MySql(3)-数据管理","abbrlink":"d709dda","date":"2021-06-07T02:43:14.000Z","description":"Mysql数据管理,增删查改","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# MySQL数据管理\n\n## 关系操作符\n\n| 运算符 | 含义 | 例子 | 结果 |\n| :------: | :----: | :--: | :---: |\n| = | 等于 | 5=6 | false |\n| <> 或 != | 不等于 | 5<>6 | true |\n| > | | | |\n| < | | | |\n| <= | | | |\n| >= | | | |\n| !< | 不小于 | | |\n| !> | 不大于 | | |\n\n\n\n## 主键\n\n主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。\n\n\n\n在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,`students`表的两行记录:\n\n| id | class_id | name | gender | score |\n| :--: | :------: | :--: | :----: | :---: |\n| 1 | 1 | 小明 | M | 90 |\n| 2 | 1 | 小红 | F | 95 |\n\n每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。\n\n假设我们把`name`字段作为主键,那么通过名字`小明`或`小红`就能唯一确定一条记录。但是,这么设定,就没法存储**同名**的同学了,因为插入相同主键的两条记录是不被允许的。\n\n\n\n对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。\n\n\n\n### 联合主键\n\n关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。\n\n对于联合主键,允许一列有重复,只要不是所有主键列都重复即可\n\n\n\n\n\n## 外键(了解)\n\n\n\n当我们用主键唯一标识记录时,我们就可以在`students`表中确定任意一个学生的记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 小明 | ... |\n| 2 | 小红 | ... |\n\n我们还可以在`classes`表中确定任意一个班级记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 一班 | ... |\n| 2 | 二班 | ... |\n\n但是我们如何确定`students`表的一条记录,例如,`id=1`的小明,属于哪个班级呢?\n\n\n\n由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个`classes`的记录可以对应多个`students`表的记录。\n\n\n\n为了表达这种一对多的关系,我们需要在`students`表中加入一列`class_id`,让它的值与`classes`表的某条记录相对应:\n\n| id | class_id | name | other columns... |\n| :--- | :------- | :--- | :--------------- |\n| 1 | 1 | 小明 | ... |\n| 2 | 1 | 小红 | ... |\n| 5 | 2 | 小白 | ... |\n\n这样,我们就可以根据`class_id`这个列直接定位出一个`students`表的记录应该对应到`classes`的哪条记录。\n\n\n\n\n\n给一张表 `students` 添加外键约束:\n\n```SQL\nALTER TABLE students\nADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意\nFOREIGN KEY (class_id) # 指定了`class_id`作为外键\nREFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)\n```\n\n\n\n\n\n添加了外键的表(`students`)为从表,被外键引用的表(`classes`)为主表,要删除主表前需先删除从表\n\n> 注意:以上外键是数据库级别外键,不推荐使用\n>\n> 最佳实现:\n>\n> - 数据库就是单纯的表,只有行(记录)、列(字段)\n> - 想使用多张表数据,想用外键(用程序如Java语言实现)\n\n\n\n## DML语言(全部记住)\n\n即数据操作语言\n\n- 插入(增)一行(记录):insert into...values...\n\n```mysql\ninsert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...;\n\n例:\n\nINSERT INTO `student` ( `id`, `name` )\nVALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');\n```\n\n![](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-26-08_image-20210419143255520.png)\n\n- 删除一列(字段):\n - delete\n - truncate\n - 相同:都会删除记录,表的结构和索引、约束不会变\n - 区别:\n 1. truncate 重新设置自增列,计数器会归零;delete不会影响自增\n 2. truncate 不会影响事务\n\n```mysql\n-- 删除一条记录\ndelete from `student` where id = '1001';\n\n-- 删除所有记录(不建议这样写)\ndelete from `student`;\n\n```\n\n```mysql\n-- 清空表的所有记录,但表的结构和索引不变\ntruncate table `student`;\n```\n\n> 对不同引擎的数据库使用`delete`语句后,重启数据库,现象:(了解)\n>\n> - InnoDB:自增列会从1开始(因为保存在内存中)\n> - MyISAM:继续从上一个自增量开始(保存在文件中)\n\n\n\n\n\n- 修改:update...set...\n\n```mysql\n-- 用where指定条件\nupdate `student` set `name`='wangwu' where `id` = `1001`;\n\n-- 不指定条件情况下,会改动所有行(即记录)\nupdate `student` set `name`='wangwu';\n\n-- 修改多个字段, 用逗号隔开\nupdate `student` set `name`='wangwu', `age`=20 where `id` = `1001`;\n\n-- 通过多个条件定位数据, 用逻辑运算符and、or...\nupdate `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;\n```\n\n> 注意 **【=】** 含义:\n>\n> 在set这样的语句后面表示赋值;\n>\n> 在where后面表示关系运算符,表示比较;\n\n\n\n## DQL查询数据(最重要)\n\nDQL(data query language:数据查询语言)\n\n- 所有查询(简单、复杂)都用它\n- 数据库中最核心、重要的语句\n- 使用频率最高的语句\n\n### 基本查询操作 select\n\n1. 查询字段\n\n ```mysql\n -- 查询全部字段 select 字段 from 表\n select * from `student`;\n \n -- 查询指定字段 select 字段1[, 字段2, ...] from 表;\n select name, age from student;\n \n -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写\n select name as `学生姓名`, age as `年龄` from `student` as `s`;\n \n -- 函数concat(a, b): 连接a、b字符串\n select concat('姓名:', name) as 新名字 from student;\n ```\n\n \n\n2. 去重 `distinct`\n\n ```mysql\n -- 去除select后重复的数据,只留下一条\n select distinct `age` from student;\n ```\n\n3. 其他\n\n ```mysql\n -- 查询系统版本\n select version(); -- version()是个函数\n \n -- 计算\n select 100*3-1 as 计算结果;\n \n -- 所有学员考试成绩 +1 分\n select `name`, `grade` + 1 as 提成1分后 from `student`; \n \n -- 查询系统内置变量值\n select @@auto_increment_increment; -- 查询自增步长\n \n ```\n\n数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。\n\n```mysql\nselect 表达式 from 表;\n```\n\n\n\n### where语句\n\n检索数据中符合条件的值\n\n| 逻辑运算符 | 语法 | 描述 |\n| :--------- | :------------------ | :-------------- |\n| and `&&` | a and b、 `a&&b` | 2个都为真返回真 |\n| or `||` | a or b 、 `a||b` | 。。。 |\n| not `!` | not a、 `!a` | 。。。 |\n\n```mysql\nSELECT\n\tid AS 学号,\n\t`name` AS 姓名 \nFROM\n\tstudent \nWHERE\n\tgrade > 80 \n\tAND age < 21;\n```\n\n```mysql\nSELECT\n\t`id` AS 学号,\n\t`name` AS 姓名,\n\t`grade` as 成绩\nFROM\n\tstudent \nWHERE\n\tgrade BETWEEN 70 AND 90;\n```\n\n### 模糊查询\n\n| 运算符 | 语法 | 描述 |\n| ----------- | --------------------- | --------------------------------------- |\n| is null | a is null | 如果a是null,则为真 |\n| is not null | a is not null | 如果a不是null,则为真 |\n| between | a between b and c | 若a在b和c之间,结果为真 |\n| like | a like b | sql匹配,若a匹配b,结果为真 |\n| in | a in(a1,a2,a3,a4,...) | 若a是a1,a2,a3,a4...中的一个值,结果为真 |\n\nlike:通常结合 `%`、`_` 来使用\n\n```mysql\n-- 查找以王字开头的name \n-- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王%'; -- 表示以【王】字开头\n\n\n-- 查找以王字开头且【名字为2个字】的name\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王__'; -- 两个下划线 \n\n\n-- 查找生日不是null的\nSELECT `id`, `name` FROM student\nWHERE `birthday` is not null;\n```\n\n\n\n### 联表查询 join on\n\n同时在2个表中查找数据(必须要有交集)\n\n\n\n\"img\"\n\n\n\n```mysql\n/**\n有2张表(都有id、name字段):\n1. `student`:班级学生基本信息\n2. `exam`:年级学生考试信息\n\n【问题:查找出本班级学生的考试信息】\n*/\n\n-- =====================================\n-- 取交集 inner join\nselect s.id as 学号, s.name as 姓名 \nfrom student as s inner join exam as e -- as可以省略\non s.id = e.id;\n\n-- =====================================\n-- 仅左边的表有,left join \nSELECT\n\ts.id 学号, -- 省略as\n\ts.NAME 姓名 \nFROM\n\tstudent s\n\tLEFT JOIN exam e ON s.NAME = e.NAME \nWHERE\n\te.`name` IS NULL;\n-- =====================================\n\n-- 两个表的所有人 \n\n```\n\n> mysql 不支持 full outer join\n>\n\n\n\n\n\n### 自连接及联表查询\n\n自己的表和自己的表连接,核心:1张表拆为2张一样的表\n\n\n\n### 分页和排序\n\n- 排序 **order by**\n\n - 升序用 ASC, 降序用DESC\n\n - order by 表示查询结果怎么排(升序、降序),通过哪个字段排\n\n 比如:\n\n ```mysql\n -- order 字段 ASC 升序\n -- order 字段 DESC 降序\n SELECT * FROM exam ORDER BY `grade` ASC; -- 成绩按升序排\n ```\n\n- 分页 **limit**:可以缓解数据库压力、给人体验更好、瀑布流\n\n - 语法:\n\n ```mysql\n -- limit 起始下标(从0开始) 页面大小\n \n -- 从第0条数据开始,显示2条数据\n SELECT * FROM exam ORDER BY `id` DESC LIMIT 0,2;\n ```\n\n \n\n\n\n### 子查询与嵌套查询\n\nwhere 表达式【这个值是计算出来的】\n\n即:where 后面还跟select语句\n\n\n\n## MySQL常用函数\n\n### 数学运算\n\n```mysql\nabs()\n\nceiling() -- 向上取整 9.1 -> 10\nfloor() -- 向下取整 9.1 -> 9\n\nrand() -- 返回0~1随机数\n\nsign() -- 判断一个数符号 0->0 负数->-1 正数->1\n```\n\n\n\n### 字符串\n\n```mysql\nchar_length() -- 字符串长度\n\nconcat('a', 'b', 'c') -- 字符串连接 返回abc\n\n-- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2\ninsert(str1, n1, len, str2)\n-- 如\nselect insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef\n\n-- 将一个字符串str1中的指定子字符串substr替换为新字符串str2\nreplace(str1, substr, str2)\n\n-- 截取指定字符串\nsubstr(str, a, len); -- 截取str中第a个开始,共len个字符的子串\n\n-- 大小写转换\nupper()\nlower()\n\n-- 返回在str第一次出现的字符串substr的索引\ninstr(str, substr) -- 没有则返回0\n\n-- 反转字符串\nreverse(str)\n\n```\n\n### 时间-日期函数\n\n```mysql\n-- 当前日期\nSELECT CURRENT_DATE()\nSELECT CURDATE()\n\nSELECT NOW(); -- 当前时间 2021-04-20 20:05:44\nSELECT LOCALTIME(); -- 本地时间\nSELECT SYSDATE(); -- 系统时间\n\nSELECT YEAR(NOW()); -- 年\nSELECT MONTH(NOW()); -- 月\nSELECT DAY(NOW());\t-- 日\nSELECT HOUR(NOW());\t-- 时\nSELECT MINUTE(NOW()); -- 分\nSELECT SECOND(NOW()); -- 秒\n```\n\n\n\n### 系统函数\n\n```mysql\nSELECT SYSTEM_USER(); -- 当前系统用户\nSELECT USER()\t\t\t-- 当前用户\nSELECT VERSION()\t\t-- 版本号\n```\n\n\n\n### 聚合函数(常用)\n\n```mysql\ncount() -- 计数有多少个记录\n\tselect count(字段) from tablename; -- 忽略所有null值\n\tselect count(*) from tablename; -- 不会忽略null值,计算行数\n\tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同\n\t\nsum()\n\tselect sum(字段) from tablename;\n\t\navg()\n\tselect avg(字段) from tablename;\n\nmax()\n\tselect max(字段) from tablename;\n\nmin()\n\tselect min(字段) from tablename;\n```\n\n\n\n\n\n> **select**语法总结\n>\n> ![image-20210420202906345](https://gitee.com/ajream/images/raw/master/img/2021-04-20 20-29-07_image-20210420202906345.png)\n\n\n\n## 数据库级别的md5摘要算法\n\n**MD5消息摘要算法**(英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】\n\nmd5不可逆,具体值的md5值是一样的\n\nmd5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样\n\nSQL使用函数 `MD5()`进行加密\n\n\n\n","source":"_posts/MySQL/3-数据管理.md","raw":"---\ntitle: MySql(3)-数据管理\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: d709dda\ndate: 2021-06-07 10:43:14\ndescription: Mysql数据管理,增删查改\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# MySQL数据管理\n\n## 关系操作符\n\n| 运算符 | 含义 | 例子 | 结果 |\n| :------: | :----: | :--: | :---: |\n| = | 等于 | 5=6 | false |\n| <> 或 != | 不等于 | 5<>6 | true |\n| > | | | |\n| < | | | |\n| <= | | | |\n| >= | | | |\n| !< | 不小于 | | |\n| !> | 不大于 | | |\n\n\n\n## 主键\n\n主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。\n\n\n\n在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,`students`表的两行记录:\n\n| id | class_id | name | gender | score |\n| :--: | :------: | :--: | :----: | :---: |\n| 1 | 1 | 小明 | M | 90 |\n| 2 | 1 | 小红 | F | 95 |\n\n每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。\n\n假设我们把`name`字段作为主键,那么通过名字`小明`或`小红`就能唯一确定一条记录。但是,这么设定,就没法存储**同名**的同学了,因为插入相同主键的两条记录是不被允许的。\n\n\n\n对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。\n\n\n\n### 联合主键\n\n关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。\n\n对于联合主键,允许一列有重复,只要不是所有主键列都重复即可\n\n\n\n\n\n## 外键(了解)\n\n\n\n当我们用主键唯一标识记录时,我们就可以在`students`表中确定任意一个学生的记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 小明 | ... |\n| 2 | 小红 | ... |\n\n我们还可以在`classes`表中确定任意一个班级记录:\n\n| id | name | other columns... |\n| :--- | :--- | :--------------- |\n| 1 | 一班 | ... |\n| 2 | 二班 | ... |\n\n但是我们如何确定`students`表的一条记录,例如,`id=1`的小明,属于哪个班级呢?\n\n\n\n由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个`classes`的记录可以对应多个`students`表的记录。\n\n\n\n为了表达这种一对多的关系,我们需要在`students`表中加入一列`class_id`,让它的值与`classes`表的某条记录相对应:\n\n| id | class_id | name | other columns... |\n| :--- | :------- | :--- | :--------------- |\n| 1 | 1 | 小明 | ... |\n| 2 | 1 | 小红 | ... |\n| 5 | 2 | 小白 | ... |\n\n这样,我们就可以根据`class_id`这个列直接定位出一个`students`表的记录应该对应到`classes`的哪条记录。\n\n\n\n\n\n给一张表 `students` 添加外键约束:\n\n```SQL\nALTER TABLE students\nADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意\nFOREIGN KEY (class_id) # 指定了`class_id`作为外键\nREFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)\n```\n\n\n\n\n\n添加了外键的表(`students`)为从表,被外键引用的表(`classes`)为主表,要删除主表前需先删除从表\n\n> 注意:以上外键是数据库级别外键,不推荐使用\n>\n> 最佳实现:\n>\n> - 数据库就是单纯的表,只有行(记录)、列(字段)\n> - 想使用多张表数据,想用外键(用程序如Java语言实现)\n\n\n\n## DML语言(全部记住)\n\n即数据操作语言\n\n- 插入(增)一行(记录):insert into...values...\n\n```mysql\ninsert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...;\n\n例:\n\nINSERT INTO `student` ( `id`, `name` )\nVALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');\n```\n\n![](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-26-08_image-20210419143255520.png)\n\n- 删除一列(字段):\n - delete\n - truncate\n - 相同:都会删除记录,表的结构和索引、约束不会变\n - 区别:\n 1. truncate 重新设置自增列,计数器会归零;delete不会影响自增\n 2. truncate 不会影响事务\n\n```mysql\n-- 删除一条记录\ndelete from `student` where id = '1001';\n\n-- 删除所有记录(不建议这样写)\ndelete from `student`;\n\n```\n\n```mysql\n-- 清空表的所有记录,但表的结构和索引不变\ntruncate table `student`;\n```\n\n> 对不同引擎的数据库使用`delete`语句后,重启数据库,现象:(了解)\n>\n> - InnoDB:自增列会从1开始(因为保存在内存中)\n> - MyISAM:继续从上一个自增量开始(保存在文件中)\n\n\n\n\n\n- 修改:update...set...\n\n```mysql\n-- 用where指定条件\nupdate `student` set `name`='wangwu' where `id` = `1001`;\n\n-- 不指定条件情况下,会改动所有行(即记录)\nupdate `student` set `name`='wangwu';\n\n-- 修改多个字段, 用逗号隔开\nupdate `student` set `name`='wangwu', `age`=20 where `id` = `1001`;\n\n-- 通过多个条件定位数据, 用逻辑运算符and、or...\nupdate `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;\n```\n\n> 注意 **【=】** 含义:\n>\n> 在set这样的语句后面表示赋值;\n>\n> 在where后面表示关系运算符,表示比较;\n\n\n\n## DQL查询数据(最重要)\n\nDQL(data query language:数据查询语言)\n\n- 所有查询(简单、复杂)都用它\n- 数据库中最核心、重要的语句\n- 使用频率最高的语句\n\n### 基本查询操作 select\n\n1. 查询字段\n\n ```mysql\n -- 查询全部字段 select 字段 from 表\n select * from `student`;\n \n -- 查询指定字段 select 字段1[, 字段2, ...] from 表;\n select name, age from student;\n \n -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写\n select name as `学生姓名`, age as `年龄` from `student` as `s`;\n \n -- 函数concat(a, b): 连接a、b字符串\n select concat('姓名:', name) as 新名字 from student;\n ```\n\n \n\n2. 去重 `distinct`\n\n ```mysql\n -- 去除select后重复的数据,只留下一条\n select distinct `age` from student;\n ```\n\n3. 其他\n\n ```mysql\n -- 查询系统版本\n select version(); -- version()是个函数\n \n -- 计算\n select 100*3-1 as 计算结果;\n \n -- 所有学员考试成绩 +1 分\n select `name`, `grade` + 1 as 提成1分后 from `student`; \n \n -- 查询系统内置变量值\n select @@auto_increment_increment; -- 查询自增步长\n \n ```\n\n数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。\n\n```mysql\nselect 表达式 from 表;\n```\n\n\n\n### where语句\n\n检索数据中符合条件的值\n\n| 逻辑运算符 | 语法 | 描述 |\n| :--------- | :------------------ | :-------------- |\n| and `&&` | a and b、 `a&&b` | 2个都为真返回真 |\n| or `||` | a or b 、 `a||b` | 。。。 |\n| not `!` | not a、 `!a` | 。。。 |\n\n```mysql\nSELECT\n\tid AS 学号,\n\t`name` AS 姓名 \nFROM\n\tstudent \nWHERE\n\tgrade > 80 \n\tAND age < 21;\n```\n\n```mysql\nSELECT\n\t`id` AS 学号,\n\t`name` AS 姓名,\n\t`grade` as 成绩\nFROM\n\tstudent \nWHERE\n\tgrade BETWEEN 70 AND 90;\n```\n\n### 模糊查询\n\n| 运算符 | 语法 | 描述 |\n| ----------- | --------------------- | --------------------------------------- |\n| is null | a is null | 如果a是null,则为真 |\n| is not null | a is not null | 如果a不是null,则为真 |\n| between | a between b and c | 若a在b和c之间,结果为真 |\n| like | a like b | sql匹配,若a匹配b,结果为真 |\n| in | a in(a1,a2,a3,a4,...) | 若a是a1,a2,a3,a4...中的一个值,结果为真 |\n\nlike:通常结合 `%`、`_` 来使用\n\n```mysql\n-- 查找以王字开头的name \n-- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王%'; -- 表示以【王】字开头\n\n\n-- 查找以王字开头且【名字为2个字】的name\nSELECT `id`, `name` FROM student\nWHERE `name` LIKE '王__'; -- 两个下划线 \n\n\n-- 查找生日不是null的\nSELECT `id`, `name` FROM student\nWHERE `birthday` is not null;\n```\n\n\n\n### 联表查询 join on\n\n同时在2个表中查找数据(必须要有交集)\n\n\n\n\"img\"\n\n\n\n```mysql\n/**\n有2张表(都有id、name字段):\n1. `student`:班级学生基本信息\n2. `exam`:年级学生考试信息\n\n【问题:查找出本班级学生的考试信息】\n*/\n\n-- =====================================\n-- 取交集 inner join\nselect s.id as 学号, s.name as 姓名 \nfrom student as s inner join exam as e -- as可以省略\non s.id = e.id;\n\n-- =====================================\n-- 仅左边的表有,left join \nSELECT\n\ts.id 学号, -- 省略as\n\ts.NAME 姓名 \nFROM\n\tstudent s\n\tLEFT JOIN exam e ON s.NAME = e.NAME \nWHERE\n\te.`name` IS NULL;\n-- =====================================\n\n-- 两个表的所有人 \n\n```\n\n> mysql 不支持 full outer join\n>\n\n\n\n\n\n### 自连接及联表查询\n\n自己的表和自己的表连接,核心:1张表拆为2张一样的表\n\n\n\n### 分页和排序\n\n- 排序 **order by**\n\n - 升序用 ASC, 降序用DESC\n\n - order by 表示查询结果怎么排(升序、降序),通过哪个字段排\n\n 比如:\n\n ```mysql\n -- order 字段 ASC 升序\n -- order 字段 DESC 降序\n SELECT * FROM exam ORDER BY `grade` ASC; -- 成绩按升序排\n ```\n\n- 分页 **limit**:可以缓解数据库压力、给人体验更好、瀑布流\n\n - 语法:\n\n ```mysql\n -- limit 起始下标(从0开始) 页面大小\n \n -- 从第0条数据开始,显示2条数据\n SELECT * FROM exam ORDER BY `id` DESC LIMIT 0,2;\n ```\n\n \n\n\n\n### 子查询与嵌套查询\n\nwhere 表达式【这个值是计算出来的】\n\n即:where 后面还跟select语句\n\n\n\n## MySQL常用函数\n\n### 数学运算\n\n```mysql\nabs()\n\nceiling() -- 向上取整 9.1 -> 10\nfloor() -- 向下取整 9.1 -> 9\n\nrand() -- 返回0~1随机数\n\nsign() -- 判断一个数符号 0->0 负数->-1 正数->1\n```\n\n\n\n### 字符串\n\n```mysql\nchar_length() -- 字符串长度\n\nconcat('a', 'b', 'c') -- 字符串连接 返回abc\n\n-- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2\ninsert(str1, n1, len, str2)\n-- 如\nselect insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef\n\n-- 将一个字符串str1中的指定子字符串substr替换为新字符串str2\nreplace(str1, substr, str2)\n\n-- 截取指定字符串\nsubstr(str, a, len); -- 截取str中第a个开始,共len个字符的子串\n\n-- 大小写转换\nupper()\nlower()\n\n-- 返回在str第一次出现的字符串substr的索引\ninstr(str, substr) -- 没有则返回0\n\n-- 反转字符串\nreverse(str)\n\n```\n\n### 时间-日期函数\n\n```mysql\n-- 当前日期\nSELECT CURRENT_DATE()\nSELECT CURDATE()\n\nSELECT NOW(); -- 当前时间 2021-04-20 20:05:44\nSELECT LOCALTIME(); -- 本地时间\nSELECT SYSDATE(); -- 系统时间\n\nSELECT YEAR(NOW()); -- 年\nSELECT MONTH(NOW()); -- 月\nSELECT DAY(NOW());\t-- 日\nSELECT HOUR(NOW());\t-- 时\nSELECT MINUTE(NOW()); -- 分\nSELECT SECOND(NOW()); -- 秒\n```\n\n\n\n### 系统函数\n\n```mysql\nSELECT SYSTEM_USER(); -- 当前系统用户\nSELECT USER()\t\t\t-- 当前用户\nSELECT VERSION()\t\t-- 版本号\n```\n\n\n\n### 聚合函数(常用)\n\n```mysql\ncount() -- 计数有多少个记录\n\tselect count(字段) from tablename; -- 忽略所有null值\n\tselect count(*) from tablename; -- 不会忽略null值,计算行数\n\tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同\n\t\nsum()\n\tselect sum(字段) from tablename;\n\t\navg()\n\tselect avg(字段) from tablename;\n\nmax()\n\tselect max(字段) from tablename;\n\nmin()\n\tselect min(字段) from tablename;\n```\n\n\n\n\n\n> **select**语法总结\n>\n> ![image-20210420202906345](https://gitee.com/ajream/images/raw/master/img/2021-04-20 20-29-07_image-20210420202906345.png)\n\n\n\n## 数据库级别的md5摘要算法\n\n**MD5消息摘要算法**(英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】\n\nmd5不可逆,具体值的md5值是一样的\n\nmd5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样\n\nSQL使用函数 `MD5()`进行加密\n\n\n\n","slug":"MySQL/3-数据管理","published":1,"updated":"2021-09-10T03:01:55.065Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6or001nakved1j01zfv","content":"

MySQL数据管理

关系操作符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符含义例子结果
=等于5=6false
<> 或 !=不等于5<>6true
>
<
<=
>=
!<不小于
!>不大于
\n
\n

主键

主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。

\n

在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,students表的两行记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idclass_idnamegenderscore
11小明M90
21小红F95
\n
\n

每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。

\n

假设我们把name字段作为主键,那么通过名字小明小红就能唯一确定一条记录。但是,这么设定,就没法存储同名的同学了,因为插入相同主键的两条记录是不被允许的。

\n

对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。

\n

联合主键

关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。

\n

对于联合主键,允许一列有重复,只要不是所有主键列都重复即可

\n

外键(了解)

当我们用主键唯一标识记录时,我们就可以在students表中确定任意一个学生的记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idnameother columns…
1小明
2小红
\n
\n

我们还可以在classes表中确定任意一个班级记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idnameother columns…
1一班
2二班
\n
\n

但是我们如何确定students表的一条记录,例如,id=1的小明,属于哪个班级呢?

\n

由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个classes的记录可以对应多个students表的记录。

\n

为了表达这种一对多的关系,我们需要在students表中加入一列class_id,让它的值与classes表的某条记录相对应:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idclass_idnameother columns…
11小明
21小红
52小白
\n
\n

这样,我们就可以根据class_id这个列直接定位出一个students表的记录应该对应到classes的哪条记录。

\n

给一张表 students 添加外键约束:

\n
1
2
3
4
ALTER TABLE students
ADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意
FOREIGN KEY (class_id) # 指定了`class_id`作为外键
REFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)
\n

添加了外键的表(students)为从表,被外键引用的表(classes)为主表,要删除主表前需先删除从表

\n
\n

注意:以上外键是数据库级别外键,不推荐使用

\n

最佳实现:

\n
    \n
  • 数据库就是单纯的表,只有行(记录)、列(字段)
  • \n
  • 想使用多张表数据,想用外键(用程序如Java语言实现)
  • \n
\n
\n

DML语言(全部记住)

即数据操作语言

\n
    \n
  • 插入(增)一行(记录):insert into…values…
  • \n
\n
1
2
3
4
5
6
insert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...;

例:

INSERT INTO `student` ( `id`, `name` )
VALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');
\n

\"\"

\n
    \n
  • 删除一列(字段):
      \n
    • delete
    • \n
    • truncate
    • \n
    • 相同:都会删除记录,表的结构和索引、约束不会变
    • \n
    • 区别:
        \n
      1. truncate 重新设置自增列,计数器会归零;delete不会影响自增
      2. \n
      3. truncate 不会影响事务
      4. \n
      \n
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
-- 删除一条记录
delete from `student` where id = '1001';

-- 删除所有记录(不建议这样写)
delete from `student`;

\n
1
2
-- 清空表的所有记录,但表的结构和索引不变
truncate table `student`;
\n
\n

对不同引擎的数据库使用delete语句后,重启数据库,现象:(了解)

\n
    \n
  • InnoDB:自增列会从1开始(因为保存在内存中)
  • \n
  • MyISAM:继续从上一个自增量开始(保存在文件中)
  • \n
\n
\n
    \n
  • 修改:update…set…
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
-- 用where指定条件
update `student` set `name`='wangwu' where `id` = `1001`;

-- 不指定条件情况下,会改动所有行(即记录)
update `student` set `name`='wangwu';

-- 修改多个字段, 用逗号隔开
update `student` set `name`='wangwu', `age`=20 where `id` = `1001`;

-- 通过多个条件定位数据, 用逻辑运算符and、or...
update `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;
\n
\n

注意 【=】 含义:

\n

在set这样的语句后面表示赋值;

\n

在where后面表示关系运算符,表示比较;

\n
\n

DQL查询数据(最重要)

DQL(data query language:数据查询语言)

\n
    \n
  • 所有查询(简单、复杂)都用它
  • \n
  • 数据库中最核心、重要的语句
  • \n
  • 使用频率最高的语句
  • \n
\n

基本查询操作 select

    \n
  1. 查询字段

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    -- 查询全部字段  select 字段 from 表
    select * from `student`;

    -- 查询指定字段 select 字段1[, 字段2, ...] from 表;
    select name, age from student;

    -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写
    select name as `学生姓名`, age as `年龄` from `student` as `s`;

    -- 函数concat(a, b): 连接a、b字符串
    select concat('姓名:', name) as 新名字 from student;
    \n
  2. \n
\n
    \n
  1. 去重 distinct

    \n
    1
    2
    -- 去除select后重复的数据,只留下一条
    select distinct `age` from student;
    \n
  2. \n
  3. 其他

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -- 查询系统版本
    select version(); -- version()是个函数

    -- 计算
    select 100*3-1 as 计算结果;

    -- 所有学员考试成绩 +1 分
    select `name`, `grade` + 1 as 提成1分后 from `student`;

    -- 查询系统内置变量值
    select @@auto_increment_increment; -- 查询自增步长

    \n
  4. \n
\n

数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。

\n
1
select 表达式 from 表;
\n

where语句

检索数据中符合条件的值

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
逻辑运算符语法描述
and &&a and b、 a&&b2个都为真返回真
or ``a or b 、 `ab`。。。
not !not a、 !a。。。
\n
\n
1
2
3
4
5
6
7
8
SELECT
\tid AS 学号,
\t`name` AS 姓名
FROM
\tstudent
WHERE
\tgrade > 80
\tAND age < 21;
\n
1
2
3
4
5
6
7
8
SELECT
\t`id` AS 学号,
\t`name` AS 姓名,
\t`grade` as 成绩
FROM
\tstudent
WHERE
\tgrade BETWEEN 70 AND 90;
\n

模糊查询

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符语法描述
is nulla is null如果a是null,则为真
is not nulla is not null如果a不是null,则为真
betweena between b and c若a在b和c之间,结果为真
likea like bsql匹配,若a匹配b,结果为真
ina in(a1,a2,a3,a4,…)若a是a1,a2,a3,a4…中的一个值,结果为真
\n
\n

like:通常结合 %_ 来使用

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 查找以王字开头的name  
-- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符
SELECT `id`, `name` FROM student
WHERE `name` LIKE '王%'; -- 表示以【王】字开头


-- 查找以王字开头且【名字为2个字】的name
SELECT `id`, `name` FROM student
WHERE `name` LIKE '王__'; -- 两个下划线


-- 查找生日不是null的
SELECT `id`, `name` FROM student
WHERE `birthday` is not null;
\n

联表查询 join on

同时在2个表中查找数据(必须要有交集)

\n

\"img\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
有2张表(都有id、name字段):
1. `student`:班级学生基本信息
2. `exam`:年级学生考试信息

【问题:查找出本班级学生的考试信息】
*/

-- =====================================
-- 取交集 inner join
select s.id as 学号, s.name as 姓名
from student as s inner join exam as e -- as可以省略
on s.id = e.id;

-- =====================================
-- 仅左边的表有,left join
SELECT
\ts.id 学号, -- 省略as
\ts.NAME 姓名
FROM
\tstudent s
\tLEFT JOIN exam e ON s.NAME = e.NAME
WHERE
\te.`name` IS NULL;
-- =====================================

-- 两个表的所有人

\n
\n

mysql 不支持 full outer join

\n
\n

自连接及联表查询

自己的表和自己的表连接,核心:1张表拆为2张一样的表

\n

分页和排序

    \n
  • 排序 order by

    \n
      \n
    • 升序用 ASC, 降序用DESC

      \n
    • \n
    • order by 表示查询结果怎么排(升序、降序),通过哪个字段排

      \n

      比如:

      \n
      1
      2
      3
      -- order 字段  ASC 升序
      -- order 字段 DESC 降序
      SELECT * FROM exam ORDER BY `grade` ASC; -- 成绩按升序排
      \n
    • \n
    \n
  • \n
  • 分页 limit:可以缓解数据库压力、给人体验更好、瀑布流

    \n
      \n
    • 语法:

      \n
      1
      2
      3
      4
      -- limit 起始下标(从0开始) 页面大小

      -- 从第0条数据开始,显示2条数据
      SELECT * FROM exam ORDER BY `id` DESC LIMIT 0,2;
      \n
    • \n
    \n
  • \n
\n

子查询与嵌套查询

where 表达式【这个值是计算出来的】

\n

即:where 后面还跟select语句

\n

MySQL常用函数

数学运算

1
2
3
4
5
6
7
8
abs()

ceiling() -- 向上取整 9.1 -> 10
floor() -- 向下取整 9.1 -> 9

rand() -- 返回0~1随机数

sign() -- 判断一个数符号 0->0 负数->-1 正数->1
\n

字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
char_length()  -- 字符串长度

concat('a', 'b', 'c') -- 字符串连接 返回abc

-- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2
insert(str1, n1, len, str2)
-- 如
select insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef

-- 将一个字符串str1中的指定子字符串substr替换为新字符串str2
replace(str1, substr, str2)

-- 截取指定字符串
substr(str, a, len); -- 截取str中第a个开始,共len个字符的子串

-- 大小写转换
upper()
lower()

-- 返回在str第一次出现的字符串substr的索引
instr(str, substr) -- 没有则返回0

-- 反转字符串
reverse(str)

\n

时间-日期函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 当前日期
SELECT CURRENT_DATE()
SELECT CURDATE()

SELECT NOW(); -- 当前时间 2021-04-20 20:05:44
SELECT LOCALTIME(); -- 本地时间
SELECT SYSDATE(); -- 系统时间

SELECT YEAR(NOW()); -- 年
SELECT MONTH(NOW()); -- 月
SELECT DAY(NOW());\t-- 日
SELECT HOUR(NOW());\t-- 时
SELECT MINUTE(NOW()); -- 分
SELECT SECOND(NOW()); -- 秒
\n

系统函数

1
2
3
SELECT SYSTEM_USER();  -- 当前系统用户
SELECT USER()\t\t\t-- 当前用户
SELECT VERSION()\t\t-- 版本号
\n

聚合函数(常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
count() -- 计数有多少个记录
\tselect count(字段) from tablename; -- 忽略所有null值
\tselect count(*) from tablename; -- 不会忽略null值,计算行数
\tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同
\t
sum()
\tselect sum(字段) from tablename;
\t
avg()
\tselect avg(字段) from tablename;

max()
\tselect max(字段) from tablename;

min()
\tselect min(字段) from tablename;
\n
\n

select语法总结

\n

\"image-20210420202906345\"

\n
\n

数据库级别的md5摘要算法

MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】

\n

md5不可逆,具体值的md5值是一样的

\n

md5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样

\n

SQL使用函数 MD5()进行加密

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

MySQL数据管理

关系操作符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符含义例子结果
=等于5=6false
<> 或 !=不等于5<>6true
>
<
<=
>=
!<不小于
!>不大于
\n
\n

主键

主键:对于关系表,有个很重要的约束,就是任意两条记录不能重复。不能重复不是指两条记录不完全相同,而是指能够通过某个字段唯一区分出不同的记录,这个字段被称为主键。

\n

在关系数据库中,一张表中的每一行数据被称为一条记录。一条记录就是由多个字段组成的。例如,students表的两行记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idclass_idnamegenderscore
11小明M90
21小红F95
\n
\n

每一条记录都包含若干定义好的字段。同一个表的所有记录都有相同的字段定义。

\n

假设我们把name字段作为主键,那么通过名字小明小红就能唯一确定一条记录。但是,这么设定,就没法存储同名的同学了,因为插入相同主键的两条记录是不被允许的。

\n

对主键的要求,最关键的一点是:记录一旦插入到表中,主键最好不要再修改,因为主键是用来唯一定位记录的,修改了主键,会造成一系列的影响。

\n

联合主键

关系数据库实际上还允许通过多个字段唯一标识记录,即两个或更多的字段都设置为主键,这种主键被称为联合主键。

\n

对于联合主键,允许一列有重复,只要不是所有主键列都重复即可

\n

外键(了解)

当我们用主键唯一标识记录时,我们就可以在students表中确定任意一个学生的记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idnameother columns…
1小明
2小红
\n
\n

我们还可以在classes表中确定任意一个班级记录:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idnameother columns…
1一班
2二班
\n
\n

但是我们如何确定students表的一条记录,例如,id=1的小明,属于哪个班级呢?

\n

由于一个班级可以有多个学生,在关系模型中,这两个表的关系可以称为“一对多”,即一个classes的记录可以对应多个students表的记录。

\n

为了表达这种一对多的关系,我们需要在students表中加入一列class_id,让它的值与classes表的某条记录相对应:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
idclass_idnameother columns…
11小明
21小红
52小白
\n
\n

这样,我们就可以根据class_id这个列直接定位出一个students表的记录应该对应到classes的哪条记录。

\n

给一张表 students 添加外键约束:

\n
1
2
3
4
ALTER TABLE students
ADD CONSTRAINT fk_class_id #外键约束的名称`fk_class_id`可以任意
FOREIGN KEY (class_id) # 指定了`class_id`作为外键
REFERENCES classes (id); #指定了这个外键将关联到`classes`表的`id`列(即`classes`表的主键)
\n

添加了外键的表(students)为从表,被外键引用的表(classes)为主表,要删除主表前需先删除从表

\n
\n

注意:以上外键是数据库级别外键,不推荐使用

\n

最佳实现:

\n
    \n
  • 数据库就是单纯的表,只有行(记录)、列(字段)
  • \n
  • 想使用多张表数据,想用外键(用程序如Java语言实现)
  • \n
\n
\n

DML语言(全部记住)

即数据操作语言

\n
    \n
  • 插入(增)一行(记录):insert into…values…
  • \n
\n
1
2
3
4
5
6
insert into `表名`([字段名1,字段名2,...]) values ('值1a', '值1b'), ('值2a', '值2b'), ...;

例:

INSERT INTO `student` ( `id`, `name` )
VALUES ( 1001, '张三' ), (1002, '李四'), (1003, '王五');
\n

\"\"

\n
    \n
  • 删除一列(字段):
      \n
    • delete
    • \n
    • truncate
    • \n
    • 相同:都会删除记录,表的结构和索引、约束不会变
    • \n
    • 区别:
        \n
      1. truncate 重新设置自增列,计数器会归零;delete不会影响自增
      2. \n
      3. truncate 不会影响事务
      4. \n
      \n
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
-- 删除一条记录
delete from `student` where id = '1001';

-- 删除所有记录(不建议这样写)
delete from `student`;

\n
1
2
-- 清空表的所有记录,但表的结构和索引不变
truncate table `student`;
\n
\n

对不同引擎的数据库使用delete语句后,重启数据库,现象:(了解)

\n
    \n
  • InnoDB:自增列会从1开始(因为保存在内存中)
  • \n
  • MyISAM:继续从上一个自增量开始(保存在文件中)
  • \n
\n
\n
    \n
  • 修改:update…set…
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
-- 用where指定条件
update `student` set `name`='wangwu' where `id` = `1001`;

-- 不指定条件情况下,会改动所有行(即记录)
update `student` set `name`='wangwu';

-- 修改多个字段, 用逗号隔开
update `student` set `name`='wangwu', `age`=20 where `id` = `1001`;

-- 通过多个条件定位数据, 用逻辑运算符and、or...
update `student` set `name`='wangwu' where `id` = `1001` and `age` = 18;
\n
\n

注意 【=】 含义:

\n

在set这样的语句后面表示赋值;

\n

在where后面表示关系运算符,表示比较;

\n
\n

DQL查询数据(最重要)

DQL(data query language:数据查询语言)

\n
    \n
  • 所有查询(简单、复杂)都用它
  • \n
  • 数据库中最核心、重要的语句
  • \n
  • 使用频率最高的语句
  • \n
\n

基本查询操作 select

    \n
  1. 查询字段

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    -- 查询全部字段  select 字段 from 表
    select * from `student`;

    -- 查询指定字段 select 字段1[, 字段2, ...] from 表;
    select name, age from student;

    -- 别名,给结果(字段、表。。。)起个名字 as, as也可以省略不写
    select name as `学生姓名`, age as `年龄` from `student` as `s`;

    -- 函数concat(a, b): 连接a、b字符串
    select concat('姓名:', name) as 新名字 from student;
    \n
  2. \n
\n
    \n
  1. 去重 distinct

    \n
    1
    2
    -- 去除select后重复的数据,只留下一条
    select distinct `age` from student;
    \n
  2. \n
  3. 其他

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -- 查询系统版本
    select version(); -- version()是个函数

    -- 计算
    select 100*3-1 as 计算结果;

    -- 所有学员考试成绩 +1 分
    select `name`, `grade` + 1 as 提成1分后 from `student`;

    -- 查询系统内置变量值
    select @@auto_increment_increment; -- 查询自增步长

    \n
  4. \n
\n

数据库的表达式:文本值、列、null、函数、计算表达式、系统变量。。。

\n
1
select 表达式 from 表;
\n

where语句

检索数据中符合条件的值

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
逻辑运算符语法描述
and &&a and b、 a&&b2个都为真返回真
or ``a or b 、 `ab`。。。
not !not a、 !a。。。
\n
\n
1
2
3
4
5
6
7
8
SELECT
\tid AS 学号,
\t`name` AS 姓名
FROM
\tstudent
WHERE
\tgrade > 80
\tAND age < 21;
\n
1
2
3
4
5
6
7
8
SELECT
\t`id` AS 学号,
\t`name` AS 姓名,
\t`grade` as 成绩
FROM
\tstudent
WHERE
\tgrade BETWEEN 70 AND 90;
\n

模糊查询

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符语法描述
is nulla is null如果a是null,则为真
is not nulla is not null如果a不是null,则为真
betweena between b and c若a在b和c之间,结果为真
likea like bsql匹配,若a匹配b,结果为真
ina in(a1,a2,a3,a4,…)若a是a1,a2,a3,a4…中的一个值,结果为真
\n
\n

like:通常结合 %_ 来使用

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 查找以王字开头的name  
-- “%”表示【任意多个】【任意的】字符,“_”表示任意【一个】字符
SELECT `id`, `name` FROM student
WHERE `name` LIKE '王%'; -- 表示以【王】字开头


-- 查找以王字开头且【名字为2个字】的name
SELECT `id`, `name` FROM student
WHERE `name` LIKE '王__'; -- 两个下划线


-- 查找生日不是null的
SELECT `id`, `name` FROM student
WHERE `birthday` is not null;
\n

联表查询 join on

同时在2个表中查找数据(必须要有交集)

\n

\"img\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
有2张表(都有id、name字段):
1. `student`:班级学生基本信息
2. `exam`:年级学生考试信息

【问题:查找出本班级学生的考试信息】
*/

-- =====================================
-- 取交集 inner join
select s.id as 学号, s.name as 姓名
from student as s inner join exam as e -- as可以省略
on s.id = e.id;

-- =====================================
-- 仅左边的表有,left join
SELECT
\ts.id 学号, -- 省略as
\ts.NAME 姓名
FROM
\tstudent s
\tLEFT JOIN exam e ON s.NAME = e.NAME
WHERE
\te.`name` IS NULL;
-- =====================================

-- 两个表的所有人

\n
\n

mysql 不支持 full outer join

\n
\n

自连接及联表查询

自己的表和自己的表连接,核心:1张表拆为2张一样的表

\n

分页和排序

    \n
  • 排序 order by

    \n
      \n
    • 升序用 ASC, 降序用DESC

      \n
    • \n
    • order by 表示查询结果怎么排(升序、降序),通过哪个字段排

      \n

      比如:

      \n
      1
      2
      3
      -- order 字段  ASC 升序
      -- order 字段 DESC 降序
      SELECT * FROM exam ORDER BY `grade` ASC; -- 成绩按升序排
      \n
    • \n
    \n
  • \n
  • 分页 limit:可以缓解数据库压力、给人体验更好、瀑布流

    \n
      \n
    • 语法:

      \n
      1
      2
      3
      4
      -- limit 起始下标(从0开始) 页面大小

      -- 从第0条数据开始,显示2条数据
      SELECT * FROM exam ORDER BY `id` DESC LIMIT 0,2;
      \n
    • \n
    \n
  • \n
\n

子查询与嵌套查询

where 表达式【这个值是计算出来的】

\n

即:where 后面还跟select语句

\n

MySQL常用函数

数学运算

1
2
3
4
5
6
7
8
abs()

ceiling() -- 向上取整 9.1 -> 10
floor() -- 向下取整 9.1 -> 9

rand() -- 返回0~1随机数

sign() -- 判断一个数符号 0->0 负数->-1 正数->1
\n

字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
char_length()  -- 字符串长度

concat('a', 'b', 'c') -- 字符串连接 返回abc

-- 字符索引从1开始,替换时,把第n1开始的共len个字符替换为str2
insert(str1, n1, len, str2)
-- 如
select insert('abcdef', 2, 3, '5555'); -- 返回 a5555ef

-- 将一个字符串str1中的指定子字符串substr替换为新字符串str2
replace(str1, substr, str2)

-- 截取指定字符串
substr(str, a, len); -- 截取str中第a个开始,共len个字符的子串

-- 大小写转换
upper()
lower()

-- 返回在str第一次出现的字符串substr的索引
instr(str, substr) -- 没有则返回0

-- 反转字符串
reverse(str)

\n

时间-日期函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 当前日期
SELECT CURRENT_DATE()
SELECT CURDATE()

SELECT NOW(); -- 当前时间 2021-04-20 20:05:44
SELECT LOCALTIME(); -- 本地时间
SELECT SYSDATE(); -- 系统时间

SELECT YEAR(NOW()); -- 年
SELECT MONTH(NOW()); -- 月
SELECT DAY(NOW());\t-- 日
SELECT HOUR(NOW());\t-- 时
SELECT MINUTE(NOW()); -- 分
SELECT SECOND(NOW()); -- 秒
\n

系统函数

1
2
3
SELECT SYSTEM_USER();  -- 当前系统用户
SELECT USER()\t\t\t-- 当前用户
SELECT VERSION()\t\t-- 版本号
\n

聚合函数(常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
count() -- 计数有多少个记录
\tselect count(字段) from tablename; -- 忽略所有null值
\tselect count(*) from tablename; -- 不会忽略null值,计算行数
\tselect count(1) from tablename; -- 不会忽略null值,与上一个基本相同
\t
sum()
\tselect sum(字段) from tablename;
\t
avg()
\tselect avg(字段) from tablename;

max()
\tselect max(字段) from tablename;

min()
\tselect min(字段) from tablename;
\n
\n

select语法总结

\n

\"image-20210420202906345\"

\n
\n

数据库级别的md5摘要算法

MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的【密码散列函数】

\n

md5不可逆,具体值的md5值是一样的

\n

md5破解网站破解原理,其背后有一个巨大的字典库,你给它一个md5值,它就会搜索字典中的哪个字符串经过md5转换后与你输入的一样

\n

SQL使用函数 MD5()进行加密

\n"},{"title":"MySql(5)-索引","abbrlink":"683d1a78","date":"2021-06-09T03:53:17.000Z","description":"MySQL数据库索引介绍与使用,索引可以提高查询效率","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# 索引\n\n**索引是**帮助MySQL高效获取数据的**数据结构**\n\n## 索引分类\n\n- 主键索引(primary key)\n\n - 唯一的标识,主键不可重复,只能有一个列为主键\n- 唯一索引(unique key)\n - 避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引\n- 常规索引(key/index)\n - 默认的,index、key关键字来设置\n- 全文索引(fulltext)\n\n\n\n\n\n## 索引使用\n\n1. 在创建表的时候给字段增加索引\n2. 创建完毕后,增加索引\n\n```mysql\n-- 显示所有索引信息\nshow index from tableA;\n\n-- 增加一个全文索引\nalter table tableA add fulltext index `studentName`;\n\n-- 分析sql执行状况 explain\nexplain select * from tableA;\n\n```\n\n\n\n\n\n## 索引使用原则\n\n- 不是越多越好\n- 不要对进程变动数据加索引\n- 小数据量的表不要添加索引\n- 索引一般加在常用来查询的字段\n\n\n\n## 索引数据结构\n\n- hash类型\n- B-tree类型(innoDB默认索引类型)\n\n\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/MySQL/5-索引.md","raw":"---\ntitle: MySql(5)-索引\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 683d1a78\ndate: 2021-06-09 11:53:17\ndescription: MySQL数据库索引介绍与使用,索引可以提高查询效率\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# 索引\n\n**索引是**帮助MySQL高效获取数据的**数据结构**\n\n## 索引分类\n\n- 主键索引(primary key)\n\n - 唯一的标识,主键不可重复,只能有一个列为主键\n- 唯一索引(unique key)\n - 避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引\n- 常规索引(key/index)\n - 默认的,index、key关键字来设置\n- 全文索引(fulltext)\n\n\n\n\n\n## 索引使用\n\n1. 在创建表的时候给字段增加索引\n2. 创建完毕后,增加索引\n\n```mysql\n-- 显示所有索引信息\nshow index from tableA;\n\n-- 增加一个全文索引\nalter table tableA add fulltext index `studentName`;\n\n-- 分析sql执行状况 explain\nexplain select * from tableA;\n\n```\n\n\n\n\n\n## 索引使用原则\n\n- 不是越多越好\n- 不要对进程变动数据加索引\n- 小数据量的表不要添加索引\n- 索引一般加在常用来查询的字段\n\n\n\n## 索引数据结构\n\n- hash类型\n- B-tree类型(innoDB默认索引类型)\n\n\n\n\n\n\n\n\n\n\n\n\n\n","slug":"MySQL/5-索引","published":1,"updated":"2021-09-08T13:05:57.536Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ou001qakve1mo0aal1","content":"

索引

索引是帮助MySQL高效获取数据的数据结构

\n

索引分类

    \n
  • 主键索引(primary key)

    \n
      \n
    • 唯一的标识,主键不可重复,只能有一个列为主键
    • \n
    \n
  • \n
  • 唯一索引(unique key)
      \n
    • 避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引
    • \n
    \n
  • \n
  • 常规索引(key/index)
      \n
    • 默认的,index、key关键字来设置
    • \n
    \n
  • \n
  • 全文索引(fulltext)
  • \n
\n

索引使用

    \n
  1. 在创建表的时候给字段增加索引
  2. \n
  3. 创建完毕后,增加索引
  4. \n
\n
1
2
3
4
5
6
7
8
9
-- 显示所有索引信息
show index from tableA;

-- 增加一个全文索引
alter table tableA add fulltext index `studentName`;

-- 分析sql执行状况 explain
explain select * from tableA;

\n

索引使用原则

    \n
  • 不是越多越好
  • \n
  • 不要对进程变动数据加索引
  • \n
  • 小数据量的表不要添加索引
  • \n
  • 索引一般加在常用来查询的字段
  • \n
\n

索引数据结构

    \n
  • hash类型
  • \n
  • B-tree类型(innoDB默认索引类型)
  • \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

索引

索引是帮助MySQL高效获取数据的数据结构

\n

索引分类

    \n
  • 主键索引(primary key)

    \n
      \n
    • 唯一的标识,主键不可重复,只能有一个列为主键
    • \n
    \n
  • \n
  • 唯一索引(unique key)
      \n
    • 避免重复的列出现,唯一索引可以重复,多个列均可以标识为唯一索引
    • \n
    \n
  • \n
  • 常规索引(key/index)
      \n
    • 默认的,index、key关键字来设置
    • \n
    \n
  • \n
  • 全文索引(fulltext)
  • \n
\n

索引使用

    \n
  1. 在创建表的时候给字段增加索引
  2. \n
  3. 创建完毕后,增加索引
  4. \n
\n
1
2
3
4
5
6
7
8
9
-- 显示所有索引信息
show index from tableA;

-- 增加一个全文索引
alter table tableA add fulltext index `studentName`;

-- 分析sql执行状况 explain
explain select * from tableA;

\n

索引使用原则

    \n
  • 不是越多越好
  • \n
  • 不要对进程变动数据加索引
  • \n
  • 小数据量的表不要添加索引
  • \n
  • 索引一般加在常用来查询的字段
  • \n
\n

索引数据结构

    \n
  • hash类型
  • \n
  • B-tree类型(innoDB默认索引类型)
  • \n
\n"},{"title":"MySql(6)-权限-备份","abbrlink":"d29e4d17","date":"2021-06-10T03:37:39.000Z","description":"关于数据库一些注意事项,如权限管理,备份管理,数据库设计、SQL注入问题","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n\n## 权限管理和备份\n\n\n\n```mysql\n-- 创建用户 create user 用户名 identified by '密码'\ncreate user 'kjn' identified by '123456';\n\n-- 修改当前用户密码\nset password = password('98765');\n\n-- 修改指定用户密码\nset password for '用户名' = password('98765');\n\n-- 重命名用户 rename ... to ...\nrename user 原来名字 to 新名字;\n\n-- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干\ngrant all privileges on *.* to 用户名;\n\n-- 查看权限\nshow grants for 用户名;\n\n\n-- 撤销权限\nrevoke all privileges on *.* from 用户名;\n\n\n```\n\n\n\n\n\n## 数据库备份\n\n备份方式\n\n- 直接拷贝物理文件\n\n- 在软件中导出\n\n navicat右键点击表导出\n\n- cmd命令行导出\n\n ```mysql\n -- mysqldump -h本机服务\n mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名\n ```\n\n- 导入(mysql命令行)\n\n ```mysql\n -- 导入D:/a.sql\n mysql > source d:/a.sql;\n ```\n\n \n\n## 规范数据库设计\n\n数据库比较复杂时就要设计\n\n**良好的数据库设计:**\n\n- 节省内存空间\n- 保证数据库完整性\n- 方便开发系统\n\n**软件开发中:**\n\n- 分析需求:分析业务和需要处理的数据库的需求\n- 概要设计:设计关系图E-R图\n\n\n\n**设计数据库步骤**(个人博客)\n\n- 收集信息,分析需求\n - 用户表(用户登录、注销、个人信息,写博客,创建分支)\n - 分类表(文章分类、谁创建的)\n - 文章表\n - 评论表\n - 粉丝表\n - 自定义表(系统信息、某个关键字、字段)\n - 说说表(用户id、内容、发表时间)\n- 标识实体(把需求落实到每个字段)\n- 标识实体之间关系\n - 写博客:user->blog\n - 创建分类:user->catagory\n - 关注:user->user\n - 粉丝链接:links\n - 评论:user->user-blog\n\n\n\n### 三大范式\n\n> 为什么需要数据规范化\n\n- 信息重写\n- 更新异常\n- 插入异常\n - 无法正常显示信息\n- 删除异常\n - 丢失有效信息\n\n\n\n#### 三大范式\n\n1. **第一范式(1NF)**\n\n 原子性:保持列不可再分\n\n2. **第二范式(2NF)**\n\n 前提:满足1NF\n\n 每张表只描述一件事\n\n3. **第三范式(3NF)**\n\n 前提:满足1NF与2NF\n\n 确保数据表中每一列数据都和主键直接相关,而不能间接相关\n\n\n\n#### 规范性和性能问题\n\n关联查询的表不能超过3张表\n\n- 考虑商业化的需求和目标(成本、用户体验),数据库性能更重要\n- 在规范性能的问题时候,需要适当考虑一下规范性\n- 可以故意给某些表增加一些冗余的字段(多表查询变为单表查询)\n- 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)\n\n\n\n\n\n## SQL注入问题\n\nsql存在漏洞,会被攻击,导致数据泄露\n\n\n\n\n\n\n\n## PrepareStatement对象\n\n防止SQL注入\n\n","source":"_posts/MySQL/6-权限-备份.md","raw":"---\ntitle: MySql(6)-权限-备份\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: d29e4d17\ndate: 2021-06-10 11:37:39\ndescription: 关于数据库一些注意事项,如权限管理,备份管理,数据库设计、SQL注入问题\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n\n## 权限管理和备份\n\n\n\n```mysql\n-- 创建用户 create user 用户名 identified by '密码'\ncreate user 'kjn' identified by '123456';\n\n-- 修改当前用户密码\nset password = password('98765');\n\n-- 修改指定用户密码\nset password for '用户名' = password('98765');\n\n-- 重命名用户 rename ... to ...\nrename user 原来名字 to 新名字;\n\n-- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干\ngrant all privileges on *.* to 用户名;\n\n-- 查看权限\nshow grants for 用户名;\n\n\n-- 撤销权限\nrevoke all privileges on *.* from 用户名;\n\n\n```\n\n\n\n\n\n## 数据库备份\n\n备份方式\n\n- 直接拷贝物理文件\n\n- 在软件中导出\n\n navicat右键点击表导出\n\n- cmd命令行导出\n\n ```mysql\n -- mysqldump -h本机服务\n mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名\n ```\n\n- 导入(mysql命令行)\n\n ```mysql\n -- 导入D:/a.sql\n mysql > source d:/a.sql;\n ```\n\n \n\n## 规范数据库设计\n\n数据库比较复杂时就要设计\n\n**良好的数据库设计:**\n\n- 节省内存空间\n- 保证数据库完整性\n- 方便开发系统\n\n**软件开发中:**\n\n- 分析需求:分析业务和需要处理的数据库的需求\n- 概要设计:设计关系图E-R图\n\n\n\n**设计数据库步骤**(个人博客)\n\n- 收集信息,分析需求\n - 用户表(用户登录、注销、个人信息,写博客,创建分支)\n - 分类表(文章分类、谁创建的)\n - 文章表\n - 评论表\n - 粉丝表\n - 自定义表(系统信息、某个关键字、字段)\n - 说说表(用户id、内容、发表时间)\n- 标识实体(把需求落实到每个字段)\n- 标识实体之间关系\n - 写博客:user->blog\n - 创建分类:user->catagory\n - 关注:user->user\n - 粉丝链接:links\n - 评论:user->user-blog\n\n\n\n### 三大范式\n\n> 为什么需要数据规范化\n\n- 信息重写\n- 更新异常\n- 插入异常\n - 无法正常显示信息\n- 删除异常\n - 丢失有效信息\n\n\n\n#### 三大范式\n\n1. **第一范式(1NF)**\n\n 原子性:保持列不可再分\n\n2. **第二范式(2NF)**\n\n 前提:满足1NF\n\n 每张表只描述一件事\n\n3. **第三范式(3NF)**\n\n 前提:满足1NF与2NF\n\n 确保数据表中每一列数据都和主键直接相关,而不能间接相关\n\n\n\n#### 规范性和性能问题\n\n关联查询的表不能超过3张表\n\n- 考虑商业化的需求和目标(成本、用户体验),数据库性能更重要\n- 在规范性能的问题时候,需要适当考虑一下规范性\n- 可以故意给某些表增加一些冗余的字段(多表查询变为单表查询)\n- 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)\n\n\n\n\n\n## SQL注入问题\n\nsql存在漏洞,会被攻击,导致数据泄露\n\n\n\n\n\n\n\n## PrepareStatement对象\n\n防止SQL注入\n\n","slug":"MySQL/6-权限-备份","published":1,"updated":"2021-09-08T13:05:51.506Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ow001uakve6hqegt4b","content":"

权限管理和备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- 创建用户 create user 用户名 identified by '密码'
create user 'kjn' identified by '123456';

-- 修改当前用户密码
set password = password('98765');

-- 修改指定用户密码
set password for '用户名' = password('98765');

-- 重命名用户 rename ... to ...
rename user 原来名字 to 新名字;

-- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干
grant all privileges on *.* to 用户名;

-- 查看权限
show grants for 用户名;


-- 撤销权限
revoke all privileges on *.* from 用户名;


\n

数据库备份

备份方式

\n
    \n
  • 直接拷贝物理文件

    \n
  • \n
  • 在软件中导出

    \n

    navicat右键点击表导出

    \n
  • \n
  • cmd命令行导出

    \n
    1
    2
    -- mysqldump -h本机服务
    mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名
    \n
  • \n
  • 导入(mysql命令行)

    \n
    1
    2
    -- 导入D:/a.sql
    mysql > source d:/a.sql;
    \n
  • \n
\n

规范数据库设计

数据库比较复杂时就要设计

\n

良好的数据库设计:

\n
    \n
  • 节省内存空间
  • \n
  • 保证数据库完整性
  • \n
  • 方便开发系统
  • \n
\n

软件开发中:

\n
    \n
  • 分析需求:分析业务和需要处理的数据库的需求
  • \n
  • 概要设计:设计关系图E-R图
  • \n
\n

设计数据库步骤(个人博客)

\n
    \n
  • 收集信息,分析需求
      \n
    • 用户表(用户登录、注销、个人信息,写博客,创建分支)
    • \n
    • 分类表(文章分类、谁创建的)
    • \n
    • 文章表
    • \n
    • 评论表
    • \n
    • 粉丝表
    • \n
    • 自定义表(系统信息、某个关键字、字段)
    • \n
    • 说说表(用户id、内容、发表时间)
    • \n
    \n
  • \n
  • 标识实体(把需求落实到每个字段)
  • \n
  • 标识实体之间关系
      \n
    • 写博客:user->blog
    • \n
    • 创建分类:user->catagory
    • \n
    • 关注:user->user
    • \n
    • 粉丝链接:links
    • \n
    • 评论:user->user-blog
    • \n
    \n
  • \n
\n

三大范式

\n

为什么需要数据规范化

\n
\n
    \n
  • 信息重写
  • \n
  • 更新异常
  • \n
  • 插入异常
      \n
    • 无法正常显示信息
    • \n
    \n
  • \n
  • 删除异常
      \n
    • 丢失有效信息
    • \n
    \n
  • \n
\n

三大范式

    \n
  1. 第一范式(1NF)

    \n

    原子性:保持列不可再分

    \n
  2. \n
  3. 第二范式(2NF)

    \n

    前提:满足1NF

    \n

    每张表只描述一件事

    \n
  4. \n
  5. 第三范式(3NF)

    \n

    前提:满足1NF与2NF

    \n

    确保数据表中每一列数据都和主键直接相关,而不能间接相关

    \n
  6. \n
\n

规范性和性能问题

关联查询的表不能超过3张表

\n
    \n
  • 考虑商业化的需求和目标(成本、用户体验),数据库性能更重要
  • \n
  • 在规范性能的问题时候,需要适当考虑一下规范性
  • \n
  • 可以故意给某些表增加一些冗余的字段(多表查询变为单表查询)
  • \n
  • 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)
  • \n
\n

SQL注入问题

sql存在漏洞,会被攻击,导致数据泄露

\n

PrepareStatement对象

防止SQL注入

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

权限管理和备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- 创建用户 create user 用户名 identified by '密码'
create user 'kjn' identified by '123456';

-- 修改当前用户密码
set password = password('98765');

-- 修改指定用户密码
set password for '用户名' = password('98765');

-- 重命名用户 rename ... to ...
rename user 原来名字 to 新名字;

-- 授权用户全部权限 [all privileges]->除了给别人授权,什么都能干
grant all privileges on *.* to 用户名;

-- 查看权限
show grants for 用户名;


-- 撤销权限
revoke all privileges on *.* from 用户名;


\n

数据库备份

备份方式

\n
    \n
  • 直接拷贝物理文件

    \n
  • \n
  • 在软件中导出

    \n

    navicat右键点击表导出

    \n
  • \n
  • cmd命令行导出

    \n
    1
    2
    -- mysqldump -h本机服务
    mysqldump -hlocalhost -u用户名 -p密码 数据库名 表名 > 导出路径/导出文件名
    \n
  • \n
  • 导入(mysql命令行)

    \n
    1
    2
    -- 导入D:/a.sql
    mysql > source d:/a.sql;
    \n
  • \n
\n

规范数据库设计

数据库比较复杂时就要设计

\n

良好的数据库设计:

\n
    \n
  • 节省内存空间
  • \n
  • 保证数据库完整性
  • \n
  • 方便开发系统
  • \n
\n

软件开发中:

\n
    \n
  • 分析需求:分析业务和需要处理的数据库的需求
  • \n
  • 概要设计:设计关系图E-R图
  • \n
\n

设计数据库步骤(个人博客)

\n
    \n
  • 收集信息,分析需求
      \n
    • 用户表(用户登录、注销、个人信息,写博客,创建分支)
    • \n
    • 分类表(文章分类、谁创建的)
    • \n
    • 文章表
    • \n
    • 评论表
    • \n
    • 粉丝表
    • \n
    • 自定义表(系统信息、某个关键字、字段)
    • \n
    • 说说表(用户id、内容、发表时间)
    • \n
    \n
  • \n
  • 标识实体(把需求落实到每个字段)
  • \n
  • 标识实体之间关系
      \n
    • 写博客:user->blog
    • \n
    • 创建分类:user->catagory
    • \n
    • 关注:user->user
    • \n
    • 粉丝链接:links
    • \n
    • 评论:user->user-blog
    • \n
    \n
  • \n
\n

三大范式

\n

为什么需要数据规范化

\n
\n
    \n
  • 信息重写
  • \n
  • 更新异常
  • \n
  • 插入异常
      \n
    • 无法正常显示信息
    • \n
    \n
  • \n
  • 删除异常
      \n
    • 丢失有效信息
    • \n
    \n
  • \n
\n

三大范式

    \n
  1. 第一范式(1NF)

    \n

    原子性:保持列不可再分

    \n
  2. \n
  3. 第二范式(2NF)

    \n

    前提:满足1NF

    \n

    每张表只描述一件事

    \n
  4. \n
  5. 第三范式(3NF)

    \n

    前提:满足1NF与2NF

    \n

    确保数据表中每一列数据都和主键直接相关,而不能间接相关

    \n
  6. \n
\n

规范性和性能问题

关联查询的表不能超过3张表

\n
    \n
  • 考虑商业化的需求和目标(成本、用户体验),数据库性能更重要
  • \n
  • 在规范性能的问题时候,需要适当考虑一下规范性
  • \n
  • 可以故意给某些表增加一些冗余的字段(多表查询变为单表查询)
  • \n
  • 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)
  • \n
\n

SQL注入问题

sql存在漏洞,会被攻击,导致数据泄露

\n

PrepareStatement对象

防止SQL注入

\n"},{"title":"MySql(7)-JDBC","abbrlink":"3f4cddeb","date":"2021-06-11T04:54:16.000Z","description":"Java中使用MySQL数据库,即JDBC","cover":"https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png","_content":"\n\n# JDBC(重点)\n\n\n\n## idea连接MySQL数据库\n\n![image-20210812215126308](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-51-30_image-20210812215126308.png)\n\n上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:\n\n![image-20210812215624487](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-56-25_image-20210812215624487.png)\n\n\n\n\n\n## 数据库驱动\n\n驱动:声卡、显卡、数据库驱动\n\n\"image-20210421193728815\"\n\n\n\n程序通过数据库驱动操作数据库\n\n\n\n## JDBC\n\n**JDBC** (Java DataBase Connection) 是通过JAVA访问数据库;\n\nSUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 `JDBC`\n\n对于开发人员来说,我们只需要掌握 JDBC 接口即可\n\n\"image-20210421194311030\"\n\n\n\n\n\n**编写程序步骤:**\n\n1. 加载驱动\n2. 连接数据库(要有连接的服务器地址、用户名、密码) `DriverManager`\n3. 获取SQL对象\n4. 执行SQL语句,获取返回结果\n5. 释放连接\n\n```java\npackage com.jdbcTest;\n\nimport java.sql.*;\n\npublic class FirstJdbcDemo {\n public static void main(String[] args) throws ClassNotFoundException,SQLException {\n // 1.加载驱动\n Class.forName(\"com.mysql.cj.jdbc.Driver\"); //固定写法,mysql5版本没有【cj】\n\n // 2.链接信息url\n String url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true\";\n String username = \"root\";\n String password = \"admin\";\n\n // 3. 连接数据库\n Connection connection = DriverManager.getConnection(url, username, password);\n\n // 获取SQL对象\n Statement statement = connection.createStatement();\n\n // 执行SQL语句,获取返回的数据\n String sql = \"SELECT * FROM `student`\";\n ResultSet resultSet = statement.executeQuery(sql); //执行\n\n while(resultSet.next()){\n System.out.println(\"id: \" + resultSet.getObject(\"id\"));\n System.out.println(\"name: \" + resultSet.getObject(\"name\"));\n System.out.println(\"email: \" + resultSet.getObject(\"email\"));\n System.out.println(\"=============================\");\n }\n\n //6. 释放连接\n resultSet.close();\n statement.close();\n connection.close();\n\n }\n\n}\n\n```\n\n\n\n> url:\n>\n> ```java\n> url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true\";\n> ```\n\n\n\n> connection 代表数据库\n\n\n\n> Statement 执行SQL语句的对象\n\n\n\n> ResultSet 获得查询结果集,有以下方法\n>\n> - next():移动到下一个记录\n> - getObject(): 获取结果(不知道字段类型时)\n> - getString(): 获取字符串字段(知道字段类型)\n\n\n\n\n\n","source":"_posts/MySQL/7-JDBC.md","raw":"---\ntitle: MySql(7)-JDBC\ntags:\n - MySQL\ncategories:\n - - java\n - MySQL\nabbrlink: 3f4cddeb\ndate: 2021-06-11 12:54:16\ndescription: Java中使用MySQL数据库,即JDBC\ncover: https://gitee.com/ajream/images/raw/master/img/20210901183224_mysql.png\n---\n\n\n# JDBC(重点)\n\n\n\n## idea连接MySQL数据库\n\n![image-20210812215126308](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-51-30_image-20210812215126308.png)\n\n上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:\n\n![image-20210812215624487](https://gitee.com/ajream/images/raw/master/img/2021-08-1221-56-25_image-20210812215624487.png)\n\n\n\n\n\n## 数据库驱动\n\n驱动:声卡、显卡、数据库驱动\n\n\"image-20210421193728815\"\n\n\n\n程序通过数据库驱动操作数据库\n\n\n\n## JDBC\n\n**JDBC** (Java DataBase Connection) 是通过JAVA访问数据库;\n\nSUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 `JDBC`\n\n对于开发人员来说,我们只需要掌握 JDBC 接口即可\n\n\"image-20210421194311030\"\n\n\n\n\n\n**编写程序步骤:**\n\n1. 加载驱动\n2. 连接数据库(要有连接的服务器地址、用户名、密码) `DriverManager`\n3. 获取SQL对象\n4. 执行SQL语句,获取返回结果\n5. 释放连接\n\n```java\npackage com.jdbcTest;\n\nimport java.sql.*;\n\npublic class FirstJdbcDemo {\n public static void main(String[] args) throws ClassNotFoundException,SQLException {\n // 1.加载驱动\n Class.forName(\"com.mysql.cj.jdbc.Driver\"); //固定写法,mysql5版本没有【cj】\n\n // 2.链接信息url\n String url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true\";\n String username = \"root\";\n String password = \"admin\";\n\n // 3. 连接数据库\n Connection connection = DriverManager.getConnection(url, username, password);\n\n // 获取SQL对象\n Statement statement = connection.createStatement();\n\n // 执行SQL语句,获取返回的数据\n String sql = \"SELECT * FROM `student`\";\n ResultSet resultSet = statement.executeQuery(sql); //执行\n\n while(resultSet.next()){\n System.out.println(\"id: \" + resultSet.getObject(\"id\"));\n System.out.println(\"name: \" + resultSet.getObject(\"name\"));\n System.out.println(\"email: \" + resultSet.getObject(\"email\"));\n System.out.println(\"=============================\");\n }\n\n //6. 释放连接\n resultSet.close();\n statement.close();\n connection.close();\n\n }\n\n}\n\n```\n\n\n\n> url:\n>\n> ```java\n> url = \"jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true\";\n> ```\n\n\n\n> connection 代表数据库\n\n\n\n> Statement 执行SQL语句的对象\n\n\n\n> ResultSet 获得查询结果集,有以下方法\n>\n> - next():移动到下一个记录\n> - getObject(): 获取结果(不知道字段类型时)\n> - getString(): 获取字符串字段(知道字段类型)\n\n\n\n\n\n","slug":"MySQL/7-JDBC","published":1,"updated":"2021-09-10T03:03:10.248Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ox001xakvegmtv0lsp","content":"

JDBC(重点)

idea连接MySQL数据库

\"image-20210812215126308\"

\n

上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:

\n

\"image-20210812215624487\"

\n

数据库驱动

驱动:声卡、显卡、数据库驱动

\n

\"image-20210421193728815\"

\n

程序通过数据库驱动操作数据库

\n

JDBC

JDBC (Java DataBase Connection) 是通过JAVA访问数据库;

\n

SUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 JDBC

\n

对于开发人员来说,我们只需要掌握 JDBC 接口即可

\n

\"image-20210421194311030\"

\n

编写程序步骤:

\n
    \n
  1. 加载驱动
  2. \n
  3. 连接数据库(要有连接的服务器地址、用户名、密码) DriverManager
  4. \n
  5. 获取SQL对象
  6. \n
  7. 执行SQL语句,获取返回结果
  8. \n
  9. 释放连接
  10. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.jdbcTest;

import java.sql.*;

public class FirstJdbcDemo {
public static void main(String[] args) throws ClassNotFoundException,SQLException {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver"); //固定写法,mysql5版本没有【cj】

// 2.链接信息url
String url = "jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true";
String username = "root";
String password = "admin";

// 3. 连接数据库
Connection connection = DriverManager.getConnection(url, username, password);

// 获取SQL对象
Statement statement = connection.createStatement();

// 执行SQL语句,获取返回的数据
String sql = "SELECT * FROM `student`";
ResultSet resultSet = statement.executeQuery(sql); //执行

while(resultSet.next()){
System.out.println("id: " + resultSet.getObject("id"));
System.out.println("name: " + resultSet.getObject("name"));
System.out.println("email: " + resultSet.getObject("email"));
System.out.println("=============================");
}

//6. 释放连接
resultSet.close();
statement.close();
connection.close();

}

}

\n
\n

url:

\n
1
url = "jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true";
\n

connection 代表数据库

\n

Statement 执行SQL语句的对象

\n

ResultSet 获得查询结果集,有以下方法

\n
    \n
  • next():移动到下一个记录
  • \n
  • getObject(): 获取结果(不知道字段类型时)
  • \n
  • getString(): 获取字符串字段(知道字段类型)
  • \n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

JDBC(重点)

idea连接MySQL数据库

\"image-20210812215126308\"

\n

上面操作完成后只会显示一个数据库mysql,要显示其他数据库,按下面操作:

\n

\"image-20210812215624487\"

\n

数据库驱动

驱动:声卡、显卡、数据库驱动

\n

\"image-20210421193728815\"

\n

程序通过数据库驱动操作数据库

\n

JDBC

JDBC (Java DataBase Connection) 是通过JAVA访问数据库;

\n

SUN公司为了简化开发人员对数据库统一的操作,提供了Java操作数据库的规范,简称 JDBC

\n

对于开发人员来说,我们只需要掌握 JDBC 接口即可

\n

\"image-20210421194311030\"

\n

编写程序步骤:

\n
    \n
  1. 加载驱动
  2. \n
  3. 连接数据库(要有连接的服务器地址、用户名、密码) DriverManager
  4. \n
  5. 获取SQL对象
  6. \n
  7. 执行SQL语句,获取返回结果
  8. \n
  9. 释放连接
  10. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.jdbcTest;

import java.sql.*;

public class FirstJdbcDemo {
public static void main(String[] args) throws ClassNotFoundException,SQLException {
// 1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver"); //固定写法,mysql5版本没有【cj】

// 2.链接信息url
String url = "jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSl=true";
String username = "root";
String password = "admin";

// 3. 连接数据库
Connection connection = DriverManager.getConnection(url, username, password);

// 获取SQL对象
Statement statement = connection.createStatement();

// 执行SQL语句,获取返回的数据
String sql = "SELECT * FROM `student`";
ResultSet resultSet = statement.executeQuery(sql); //执行

while(resultSet.next()){
System.out.println("id: " + resultSet.getObject("id"));
System.out.println("name: " + resultSet.getObject("name"));
System.out.println("email: " + resultSet.getObject("email"));
System.out.println("=============================");
}

//6. 释放连接
resultSet.close();
statement.close();
connection.close();

}

}

\n
\n

url:

\n
1
url = "jdbc:mysql://localhost:3306/school?useUnicode=true&charactorEncoding=utf8&useSSl=true";
\n

connection 代表数据库

\n

Statement 执行SQL语句的对象

\n

ResultSet 获得查询结果集,有以下方法

\n
    \n
  • next():移动到下一个记录
  • \n
  • getObject(): 获取结果(不知道字段类型时)
  • \n
  • getString(): 获取字符串字段(知道字段类型)
  • \n
\n
\n"},{"title":"Spring(1)-框架介绍","abbrlink":"3afd7950","date":"2021-07-23T04:11:32.000Z","description":"Java的spring框架介绍","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# spring框架介绍\n\n\n\n## spring是什么\n\n1. spring框架是一个开源JavaEE应用程序\n\n2. 主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西\n3. spring是基于分布式的应用程序\n - 基于轻量级框架\n - 配置管理\n - Bean对象实例化-IOC\n - 集成第三方的框架\n - mybatis\n - springMVC\n - ……\n - 自带服务\n - 邮件mail发送\n - 定时任务\n - 消息处理(异步处理)\n\n\n\n## spring结构\n\n1. Dao层:\n - jdbc操作\n - 对应框架:mybatis\n2. Service层\n - 没有对应框架\n3. controller层\n - servlet(接收请求 响应数据 地址匹配 页面转发)\n - 对应框架:springMVC\n\n\n\n## spring优点\n\n- 控制反转(IOC),面向切面编程(AOP)\n- 轻量,非入侵\n- 支持事务\n\n一句话:spring就是一个轻量级的**控制反转**和**面向切面编程**的框架\n\n\n\n\n\n## spring模块划分\n\n- SpringIOC\n- springAOP\n- springJDBC+事务\n- springWeb\n\n\n\n","source":"_posts/Spring/1-spring框架介绍.md","raw":"---\ntitle: Spring(1)-框架介绍\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 3afd7950\ndate: 2021-07-23 12:11:32\ndescription: Java的spring框架介绍\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# spring框架介绍\n\n\n\n## spring是什么\n\n1. spring框架是一个开源JavaEE应用程序\n\n2. 主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西\n3. spring是基于分布式的应用程序\n - 基于轻量级框架\n - 配置管理\n - Bean对象实例化-IOC\n - 集成第三方的框架\n - mybatis\n - springMVC\n - ……\n - 自带服务\n - 邮件mail发送\n - 定时任务\n - 消息处理(异步处理)\n\n\n\n## spring结构\n\n1. Dao层:\n - jdbc操作\n - 对应框架:mybatis\n2. Service层\n - 没有对应框架\n3. controller层\n - servlet(接收请求 响应数据 地址匹配 页面转发)\n - 对应框架:springMVC\n\n\n\n## spring优点\n\n- 控制反转(IOC),面向切面编程(AOP)\n- 轻量,非入侵\n- 支持事务\n\n一句话:spring就是一个轻量级的**控制反转**和**面向切面编程**的框架\n\n\n\n\n\n## spring模块划分\n\n- SpringIOC\n- springAOP\n- springJDBC+事务\n- springWeb\n\n\n\n","slug":"Spring/1-spring框架介绍","published":1,"updated":"2021-09-10T03:17:21.676Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6oy0020akvegdxh7rqn","content":"

spring框架介绍

spring是什么

    \n
  1. spring框架是一个开源JavaEE应用程序

    \n
  2. \n
  3. 主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西

    \n
  4. \n
  5. spring是基于分布式的应用程序
      \n
    • 基于轻量级框架
        \n
      • 配置管理
      • \n
      • Bean对象实例化-IOC
      • \n
      \n
    • \n
    • 集成第三方的框架
        \n
      • mybatis
      • \n
      • springMVC
      • \n
      • ……
      • \n
      \n
    • \n
    • 自带服务
        \n
      • 邮件mail发送
      • \n
      • 定时任务
      • \n
      • 消息处理(异步处理)
      • \n
      \n
    • \n
    \n
  6. \n
\n

spring结构

    \n
  1. Dao层:
      \n
    • jdbc操作
    • \n
    • 对应框架:mybatis
    • \n
    \n
  2. \n
  3. Service层
      \n
    • 没有对应框架
    • \n
    \n
  4. \n
  5. controller层
      \n
    • servlet(接收请求 响应数据 地址匹配 页面转发)
    • \n
    • 对应框架:springMVC
    • \n
    \n
  6. \n
\n

spring优点

    \n
  • 控制反转(IOC),面向切面编程(AOP)
  • \n
  • 轻量,非入侵
  • \n
  • 支持事务
  • \n
\n

一句话:spring就是一个轻量级的控制反转面向切面编程的框架

\n

spring模块划分

    \n
  • SpringIOC
  • \n
  • springAOP
  • \n
  • springJDBC+事务
  • \n
  • springWeb
  • \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

spring框架介绍

spring是什么

    \n
  1. spring框架是一个开源JavaEE应用程序

    \n
  2. \n
  3. 主要核心是IOC(控制反转/依赖注入)和AOP(面向切面编程),除此还有一些如springJDBC+事务等的东西

    \n
  4. \n
  5. spring是基于分布式的应用程序
      \n
    • 基于轻量级框架
        \n
      • 配置管理
      • \n
      • Bean对象实例化-IOC
      • \n
      \n
    • \n
    • 集成第三方的框架
        \n
      • mybatis
      • \n
      • springMVC
      • \n
      • ……
      • \n
      \n
    • \n
    • 自带服务
        \n
      • 邮件mail发送
      • \n
      • 定时任务
      • \n
      • 消息处理(异步处理)
      • \n
      \n
    • \n
    \n
  6. \n
\n

spring结构

    \n
  1. Dao层:
      \n
    • jdbc操作
    • \n
    • 对应框架:mybatis
    • \n
    \n
  2. \n
  3. Service层
      \n
    • 没有对应框架
    • \n
    \n
  4. \n
  5. controller层
      \n
    • servlet(接收请求 响应数据 地址匹配 页面转发)
    • \n
    • 对应框架:springMVC
    • \n
    \n
  6. \n
\n

spring优点

    \n
  • 控制反转(IOC),面向切面编程(AOP)
  • \n
  • 轻量,非入侵
  • \n
  • 支持事务
  • \n
\n

一句话:spring就是一个轻量级的控制反转面向切面编程的框架

\n

spring模块划分

    \n
  • SpringIOC
  • \n
  • springAOP
  • \n
  • springJDBC+事务
  • \n
  • springWeb
  • \n
\n"},{"title":"Spring(10)-注解实现自动装配","abbrlink":"39f00355","date":"2021-07-28T12:52:40.000Z","description":"spring使用注解来完成自动装配","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n\n\n\n# 使用注解实现自动装配\n\n\n\n[点击跳转到工程](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master1)\n\n\n\nspring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入\n\n```java\n@Autowired //通过byType实现自动装配,而且必须要求这个对象存在\n@Resource\t\t//默认通过byName实现自动装配,如果找不到名字,就通过byType自动装配,2个都不行的话就报错\n```\n\n\n\n### 使用@Autowired注入\n\n要使用注解,首先要在xml文件中提供注解支持\n\n![image-20210801104937636](https://gitee.com/ajream/images/raw/master/img/2021-08-0110-49-42_image-20210801104937636.png)\n\n\n\nbeans.xml\n\n```xml\n\n\n\n \n\n \n \n \n\n\n\n```\n\n> 注意:使用byType自动装配时,若有多个相同类型的bean,把`primary`属性值设为true说明首先使用该bean\n\n\n\n在`Person`类中:(可以没有setter方法)\n\n```java\npublic class Person {\n @Autowired\n private Cat cat;\n//...\n}\n```\n\n\n\n@Autowired就相当于:\n\n```java\n\n```\n\n\n\n还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:\n\n```java\npublic class SimpleMovieLister {\n\n private Cat cat;\n\n @Autowired\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n // ...\n}\n```\n\n\n\n由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)\n\n```xml\n \n\n \n\n \n```\n\n> primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。\n\n### @Qualifier\n\n\n\n当有多个bean类型相同,但id不同时,可以使用`@Qualifier` 来指定使用哪一个bean\n\n```java\n\npublic class Person {\n @Autowired\n @Qualifier(value = \"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n\n\n### @Nullable\n\n字段使用了这个注解,说明该字段可以为 null\n\n### 使用@Resource 注入\n\n@Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。\n\n\n\n```java\n\npublic class Person {\n @Resource(name=\"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n","source":"_posts/Spring/10-注解实现自动装配.md","raw":"---\ntitle: Spring(10)-注解实现自动装配\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 39f00355\ndate: 2021-07-28 20:52:40\ndescription: spring使用注解来完成自动装配\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n\n\n\n# 使用注解实现自动装配\n\n\n\n[点击跳转到工程](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master1)\n\n\n\nspring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入\n\n```java\n@Autowired //通过byType实现自动装配,而且必须要求这个对象存在\n@Resource\t\t//默认通过byName实现自动装配,如果找不到名字,就通过byType自动装配,2个都不行的话就报错\n```\n\n\n\n### 使用@Autowired注入\n\n要使用注解,首先要在xml文件中提供注解支持\n\n![image-20210801104937636](https://gitee.com/ajream/images/raw/master/img/2021-08-0110-49-42_image-20210801104937636.png)\n\n\n\nbeans.xml\n\n```xml\n\n\n\n \n\n \n \n \n\n\n\n```\n\n> 注意:使用byType自动装配时,若有多个相同类型的bean,把`primary`属性值设为true说明首先使用该bean\n\n\n\n在`Person`类中:(可以没有setter方法)\n\n```java\npublic class Person {\n @Autowired\n private Cat cat;\n//...\n}\n```\n\n\n\n@Autowired就相当于:\n\n```java\n\n```\n\n\n\n还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:\n\n```java\npublic class SimpleMovieLister {\n\n private Cat cat;\n\n @Autowired\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n // ...\n}\n```\n\n\n\n由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)\n\n```xml\n \n\n \n\n \n```\n\n> primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。\n\n### @Qualifier\n\n\n\n当有多个bean类型相同,但id不同时,可以使用`@Qualifier` 来指定使用哪一个bean\n\n```java\n\npublic class Person {\n @Autowired\n @Qualifier(value = \"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n\n\n### @Nullable\n\n字段使用了这个注解,说明该字段可以为 null\n\n### 使用@Resource 注入\n\n@Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。\n\n\n\n```java\n\npublic class Person {\n @Resource(name=\"cat22\")\n private Cat cat;\n//...\n}\n```\n\n```xml\n\n\n\n\n```\n\n","slug":"Spring/10-注解实现自动装配","published":1,"updated":"2021-09-10T03:17:03.959Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p60024akvegkx6f24q","content":"

使用注解实现自动装配

点击跳转到工程

\n

spring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入

\n
1
2
@Autowired   //通过byType实现自动装配,而且必须要求这个对象存在
@Resource\t\t//默认通过byName实现自动装配,如果找不到名字,就通过byType自动装配,2个都不行的话就报错
\n

使用@Autowired注入

要使用注解,首先要在xml文件中提供注解支持

\n

\"image-20210801104937636\"

\n

beans.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

<bean id="dog" class="com.ajream.pojo.Dog"/>
<bean id="cat" class="com.ajream.pojo.Cat"/>
<bean id="person" class="com.ajream.pojo.Person" />


</beans>
\n
\n

注意:使用byType自动装配时,若有多个相同类型的bean,把primary属性值设为true说明首先使用该bean

\n
\n

Person类中:(可以没有setter方法)

\n
1
2
3
4
5
public class Person {
@Autowired
private Cat cat;
//...
}
\n

@Autowired就相当于:

\n
1
<bean class="com.ajream.pojo.Person"  autowire="byType"/>
\n

还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:

\n
1
2
3
4
5
6
7
8
9
10
11
public class SimpleMovieLister {

private Cat cat;

@Autowired
public void setCat(Cat cat) {
this.cat = cat;
}

// ...
}
\n

由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)

\n
1
2
3
4
5
<bean class="example.SimpleMovieCatalog" primary="true" />

<bean class="example.SimpleMovieCatalog"/>

<bean id="movieRecommender" class="example.MovieRecommender"/>
\n
\n

primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。

\n
\n

@Qualifier

当有多个bean类型相同,但id不同时,可以使用@Qualifier 来指定使用哪一个bean

\n
1
2
3
4
5
6
7

public class Person {
@Autowired
@Qualifier(value = "cat22")
private Cat cat;
//...
}
\n
1
2
3
4
<bean id="cat22" class="com.ajream.pojo.Cat"/>
<bean id="cat23" class="com.ajream.pojo.Cat"/>

<bean class="com.ajream.pojo.Person" autowire="byType"/>
\n

@Nullable

字段使用了这个注解,说明该字段可以为 null

\n

使用@Resource 注入

@Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。

\n
1
2
3
4
5
6

public class Person {
@Resource(name="cat22")
private Cat cat;
//...
}
\n
1
2
3
4
<bean id="cat22" class="com.ajream.pojo.Cat"/>
<bean id="cat23" class="com.ajream.pojo.Cat"/>

<bean class="com.ajream.pojo.Person" autowire="byType"/>
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

使用注解实现自动装配

点击跳转到工程

\n

spring除了用xml配置文件来实现属性注入以外,还可以使用注解实现注入

\n
1
2
@Autowired   //通过byType实现自动装配,而且必须要求这个对象存在
@Resource\t\t//默认通过byName实现自动装配,如果找不到名字,就通过byType自动装配,2个都不行的话就报错
\n

使用@Autowired注入

要使用注解,首先要在xml文件中提供注解支持

\n

\"image-20210801104937636\"

\n

beans.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

<bean id="dog" class="com.ajream.pojo.Dog"/>
<bean id="cat" class="com.ajream.pojo.Cat"/>
<bean id="person" class="com.ajream.pojo.Person" />


</beans>
\n
\n

注意:使用byType自动装配时,若有多个相同类型的bean,把primary属性值设为true说明首先使用该bean

\n
\n

Person类中:(可以没有setter方法)

\n
1
2
3
4
5
public class Person {
@Autowired
private Cat cat;
//...
}
\n

@Autowired就相当于:

\n
1
<bean class="com.ajream.pojo.Person"  autowire="byType"/>
\n

还可以将 @Autowired 注释应用于传统的 setter 方法,如以下示例所示:

\n
1
2
3
4
5
6
7
8
9
10
11
public class SimpleMovieLister {

private Cat cat;

@Autowired
public void setCat(Cat cat) {
this.cat = cat;
}

// ...
}
\n

由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。因此可以使在bean中配置 primary属性 (下面是官方例子)

\n
1
2
3
4
5
<bean class="example.SimpleMovieCatalog" primary="true" />

<bean class="example.SimpleMovieCatalog"/>

<bean id="movieRecommender" class="example.MovieRecommender"/>
\n
\n

primary 表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个主要 bean,则它成为自动装配的值。

\n
\n

@Qualifier

当有多个bean类型相同,但id不同时,可以使用@Qualifier 来指定使用哪一个bean

\n
1
2
3
4
5
6
7

public class Person {
@Autowired
@Qualifier(value = "cat22")
private Cat cat;
//...
}
\n
1
2
3
4
<bean id="cat22" class="com.ajream.pojo.Cat"/>
<bean id="cat23" class="com.ajream.pojo.Cat"/>

<bean class="com.ajream.pojo.Person" autowire="byType"/>
\n

@Nullable

字段使用了这个注解,说明该字段可以为 null

\n

使用@Resource 注入

@Resource 采用 name 属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。

\n
1
2
3
4
5
6

public class Person {
@Resource(name="cat22")
private Cat cat;
//...
}
\n
1
2
3
4
<bean id="cat22" class="com.ajream.pojo.Cat"/>
<bean id="cat23" class="com.ajream.pojo.Cat"/>

<bean class="com.ajream.pojo.Person" autowire="byType"/>
\n"},{"title":"Spring(12)-静态代理","abbrlink":"be39d4e8","date":"2021-07-29T11:42:38.000Z","description":"Java静态代理模式举例介绍","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 静态代理\n\n## 角色分析\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master)\n\n- 抽象角色:用接口或抽象类解决\n\n ```java\n package com.ajream.demo01;\n \n public interface Rent {\n public void rent();\n }\n \n ```\n\n \n\n- 真实角色:被代理角色,比如房东\n\n ```java\n package com.ajream.demo01;\n \n public class Host implements Rent{\n @Override\n public void rent() {\n System.out.println(\"房东出租了房子\");\n }\n }\n \n ```\n\n \n\n- 代理角色:代理真实角色,一般会有些附属操作\n\n ```java\n package com.ajream.demo01;\n \n public class Proxy implements Rent{\n \n Host host = new Host();\n \n public Proxy() {\n }\n \n public Proxy(Host host) {\n this.host = host;\n }\n \n // 中介帮房东租房子,但中介还会有一些附属操作\n public void rent() {\n host.rent();\n seeHouse();\n qianHeTong();\n }\n \n //中介的附属操作\n public void seeHouse(){\n System.out.println(\"中介带你看房子。。。\");\n }\n \n public void qianHeTong(){\n System.out.println(\"签合同。。。\");\n }\n }\n \n ```\n\n \n\n- 客户:访问代理对象的人\n\n ```java\n package com.ajream.demo01;\n \n import org.junit.Test;\n \n public class Client {\n \n @Test\n public void test(){\n // 找房东租房子\n Host host = new Host();\n \n \n // 客户要租入房子,所以把房东丢给中介,让中介帮忙\n Proxy p = new Proxy(host);\n p.rent();\n }\n \n }\n \n ```\n\n \n\n\n\n> 代理模式好处:\n>\n> 1. 角色操作更加纯粹,不用取关注一些公共业务\n> 2. 公共业务交给代理角色,实现业务分工\n> 3. 公共业务发生扩展时,方便集中管理\n>\n> 缺点:\n>\n> 1. 一个真实角色会产生一个代理角色,代码量翻倍\n\n\n\n## 代理模式实现业务\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master2)\n\n\n\n用代理模式为业务添加日志打印功能\n\n\n\n业务接口\n\n```java\npackage com.ajream.demo02;\n\n//业务接口\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n\n```\n\n\n\n业务实现类(实现增删改查功能)\n\n```java\npackage com.ajream.demo02;\n\n//业务实现\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n\n\n【重点】业务代理类(添加日志打印功能)\n\n```java\npackage com.ajream.demo02;\n\npublic class UserServiceProxy implements UserService{\n\n UserServiceImpl userService;\n\n public UserServiceProxy() {\n }\n\n public void setUserService(UserServiceImpl userService) {\n this.userService = userService;\n }\n\n @Override\n public void add() {\n log(\"add\");\n userService.add();\n }\n\n @Override\n public void delete() {\n log(\"delete\");\n userService.delete();\n }\n\n @Override\n public void update() {\n log(\"update\");\n userService.update();\n }\n\n @Override\n public void query() {\n log(\"query\");\n userService.query();\n }\n\n //代理的额外操作,打印日志\n public void log(String msg){\n System.out.println(\"使用了\" + msg + \"方法\" );\n }\n}\n\n```\n\n\n\n客户\n\n```java\npackage com.ajream.demo02;\n\nimport org.junit.Test;\n\npublic class Client {\n @Test\n public void test1(){\n UserServiceImpl userService = new UserServiceImpl();\n\n UserServiceProxy proxy = new UserServiceProxy();\n proxy.setUserService(userService);\n\n proxy.add();\n }\n}\n\n```\n\n","source":"_posts/Spring/12-静态代理.md","raw":"---\ntitle: Spring(12)-静态代理\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: be39d4e8\ndate: 2021-07-29 19:42:38\ndescription: Java静态代理模式举例介绍\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 静态代理\n\n## 角色分析\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master)\n\n- 抽象角色:用接口或抽象类解决\n\n ```java\n package com.ajream.demo01;\n \n public interface Rent {\n public void rent();\n }\n \n ```\n\n \n\n- 真实角色:被代理角色,比如房东\n\n ```java\n package com.ajream.demo01;\n \n public class Host implements Rent{\n @Override\n public void rent() {\n System.out.println(\"房东出租了房子\");\n }\n }\n \n ```\n\n \n\n- 代理角色:代理真实角色,一般会有些附属操作\n\n ```java\n package com.ajream.demo01;\n \n public class Proxy implements Rent{\n \n Host host = new Host();\n \n public Proxy() {\n }\n \n public Proxy(Host host) {\n this.host = host;\n }\n \n // 中介帮房东租房子,但中介还会有一些附属操作\n public void rent() {\n host.rent();\n seeHouse();\n qianHeTong();\n }\n \n //中介的附属操作\n public void seeHouse(){\n System.out.println(\"中介带你看房子。。。\");\n }\n \n public void qianHeTong(){\n System.out.println(\"签合同。。。\");\n }\n }\n \n ```\n\n \n\n- 客户:访问代理对象的人\n\n ```java\n package com.ajream.demo01;\n \n import org.junit.Test;\n \n public class Client {\n \n @Test\n public void test(){\n // 找房东租房子\n Host host = new Host();\n \n \n // 客户要租入房子,所以把房东丢给中介,让中介帮忙\n Proxy p = new Proxy(host);\n p.rent();\n }\n \n }\n \n ```\n\n \n\n\n\n> 代理模式好处:\n>\n> 1. 角色操作更加纯粹,不用取关注一些公共业务\n> 2. 公共业务交给代理角色,实现业务分工\n> 3. 公共业务发生扩展时,方便集中管理\n>\n> 缺点:\n>\n> 1. 一个真实角色会产生一个代理角色,代码量翻倍\n\n\n\n## 代理模式实现业务\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master2)\n\n\n\n用代理模式为业务添加日志打印功能\n\n\n\n业务接口\n\n```java\npackage com.ajream.demo02;\n\n//业务接口\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n\n```\n\n\n\n业务实现类(实现增删改查功能)\n\n```java\npackage com.ajream.demo02;\n\n//业务实现\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n\n\n【重点】业务代理类(添加日志打印功能)\n\n```java\npackage com.ajream.demo02;\n\npublic class UserServiceProxy implements UserService{\n\n UserServiceImpl userService;\n\n public UserServiceProxy() {\n }\n\n public void setUserService(UserServiceImpl userService) {\n this.userService = userService;\n }\n\n @Override\n public void add() {\n log(\"add\");\n userService.add();\n }\n\n @Override\n public void delete() {\n log(\"delete\");\n userService.delete();\n }\n\n @Override\n public void update() {\n log(\"update\");\n userService.update();\n }\n\n @Override\n public void query() {\n log(\"query\");\n userService.query();\n }\n\n //代理的额外操作,打印日志\n public void log(String msg){\n System.out.println(\"使用了\" + msg + \"方法\" );\n }\n}\n\n```\n\n\n\n客户\n\n```java\npackage com.ajream.demo02;\n\nimport org.junit.Test;\n\npublic class Client {\n @Test\n public void test1(){\n UserServiceImpl userService = new UserServiceImpl();\n\n UserServiceProxy proxy = new UserServiceProxy();\n proxy.setUserService(userService);\n\n proxy.add();\n }\n}\n\n```\n\n","slug":"Spring/12-静态代理","published":1,"updated":"2021-09-10T03:16:52.129Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p70028akve23cieqja","content":"

静态代理

角色分析

项目地址

\n
    \n
  • 抽象角色:用接口或抽象类解决

    \n
    1
    2
    3
    4
    5
    6
    package com.ajream.demo01;

    public interface Rent {
    public void rent();
    }

    \n
  • \n
\n
    \n
  • 真实角色:被代理角色,比如房东

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.ajream.demo01;

    public class Host implements Rent{
    @Override
    public void rent() {
    System.out.println("房东出租了房子");
    }
    }

    \n
  • \n
\n
    \n
  • 代理角色:代理真实角色,一般会有些附属操作

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package com.ajream.demo01;

    public class Proxy implements Rent{

    Host host = new Host();

    public Proxy() {
    }

    public Proxy(Host host) {
    this.host = host;
    }

    // 中介帮房东租房子,但中介还会有一些附属操作
    public void rent() {
    host.rent();
    seeHouse();
    qianHeTong();
    }

    //中介的附属操作
    public void seeHouse(){
    System.out.println("中介带你看房子。。。");
    }

    public void qianHeTong(){
    System.out.println("签合同。。。");
    }
    }

    \n
  • \n
\n
    \n
  • 客户:访问代理对象的人

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.ajream.demo01;

    import org.junit.Test;

    public class Client {

    @Test
    public void test(){
    // 找房东租房子
    Host host = new Host();


    // 客户要租入房子,所以把房东丢给中介,让中介帮忙
    Proxy p = new Proxy(host);
    p.rent();
    }

    }

    \n
  • \n
\n
\n

代理模式好处:

\n
    \n
  1. 角色操作更加纯粹,不用取关注一些公共业务
  2. \n
  3. 公共业务交给代理角色,实现业务分工
  4. \n
  5. 公共业务发生扩展时,方便集中管理
  6. \n
\n

缺点:

\n
    \n
  1. 一个真实角色会产生一个代理角色,代码量翻倍
  2. \n
\n
\n

代理模式实现业务

项目地址

\n

用代理模式为业务添加日志打印功能

\n

业务接口

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.demo02;

//业务接口
public interface UserService {
void add();
void delete();
void update();
void query();
}

\n

业务实现类(实现增删改查功能)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.ajream.demo02;

//业务实现
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加一个用户");
}

@Override
public void delete() {
System.out.println("删除一个用户");
}

@Override
public void update() {
System.out.println("修改一个用户");
}

@Override
public void query() {
System.out.println("查询一个用户");
}
}

\n

【重点】业务代理类(添加日志打印功能)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.ajream.demo02;

public class UserServiceProxy implements UserService{

UserServiceImpl userService;

public UserServiceProxy() {
}

public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}

@Override
public void add() {
log("add");
userService.add();
}

@Override
public void delete() {
log("delete");
userService.delete();
}

@Override
public void update() {
log("update");
userService.update();
}

@Override
public void query() {
log("query");
userService.query();
}

//代理的额外操作,打印日志
public void log(String msg){
System.out.println("使用了" + msg + "方法" );
}
}

\n

客户

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.demo02;

import org.junit.Test;

public class Client {
@Test
public void test1(){
UserServiceImpl userService = new UserServiceImpl();

UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);

proxy.add();
}
}

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

静态代理

角色分析

项目地址

\n
    \n
  • 抽象角色:用接口或抽象类解决

    \n
    1
    2
    3
    4
    5
    6
    package com.ajream.demo01;

    public interface Rent {
    public void rent();
    }

    \n
  • \n
\n
    \n
  • 真实角色:被代理角色,比如房东

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.ajream.demo01;

    public class Host implements Rent{
    @Override
    public void rent() {
    System.out.println("房东出租了房子");
    }
    }

    \n
  • \n
\n
    \n
  • 代理角色:代理真实角色,一般会有些附属操作

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package com.ajream.demo01;

    public class Proxy implements Rent{

    Host host = new Host();

    public Proxy() {
    }

    public Proxy(Host host) {
    this.host = host;
    }

    // 中介帮房东租房子,但中介还会有一些附属操作
    public void rent() {
    host.rent();
    seeHouse();
    qianHeTong();
    }

    //中介的附属操作
    public void seeHouse(){
    System.out.println("中介带你看房子。。。");
    }

    public void qianHeTong(){
    System.out.println("签合同。。。");
    }
    }

    \n
  • \n
\n
    \n
  • 客户:访问代理对象的人

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.ajream.demo01;

    import org.junit.Test;

    public class Client {

    @Test
    public void test(){
    // 找房东租房子
    Host host = new Host();


    // 客户要租入房子,所以把房东丢给中介,让中介帮忙
    Proxy p = new Proxy(host);
    p.rent();
    }

    }

    \n
  • \n
\n
\n

代理模式好处:

\n
    \n
  1. 角色操作更加纯粹,不用取关注一些公共业务
  2. \n
  3. 公共业务交给代理角色,实现业务分工
  4. \n
  5. 公共业务发生扩展时,方便集中管理
  6. \n
\n

缺点:

\n
    \n
  1. 一个真实角色会产生一个代理角色,代码量翻倍
  2. \n
\n
\n

代理模式实现业务

项目地址

\n

用代理模式为业务添加日志打印功能

\n

业务接口

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.demo02;

//业务接口
public interface UserService {
void add();
void delete();
void update();
void query();
}

\n

业务实现类(实现增删改查功能)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.ajream.demo02;

//业务实现
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加一个用户");
}

@Override
public void delete() {
System.out.println("删除一个用户");
}

@Override
public void update() {
System.out.println("修改一个用户");
}

@Override
public void query() {
System.out.println("查询一个用户");
}
}

\n

【重点】业务代理类(添加日志打印功能)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.ajream.demo02;

public class UserServiceProxy implements UserService{

UserServiceImpl userService;

public UserServiceProxy() {
}

public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}

@Override
public void add() {
log("add");
userService.add();
}

@Override
public void delete() {
log("delete");
userService.delete();
}

@Override
public void update() {
log("update");
userService.update();
}

@Override
public void query() {
log("query");
userService.query();
}

//代理的额外操作,打印日志
public void log(String msg){
System.out.println("使用了" + msg + "方法" );
}
}

\n

客户

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.demo02;

import org.junit.Test;

public class Client {
@Test
public void test1(){
UserServiceImpl userService = new UserServiceImpl();

UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);

proxy.add();
}
}

\n"},{"title":"Spring(11)-使用注解开发","abbrlink":"38b3e585","date":"2021-07-28T12:55:13.000Z","description":"spring使用注解开发,一种还要依赖xml配置文件,另一种可以完全抛弃xml配置文件,使用纯Java来完成属性注入","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 使用注解开发\n\n## 依然使用xml配置文件来管理\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master3/)\n\nspring4之后,要使用注解开发,必须保证aop的包导入了\n\n![image-20210801135559755](https://gitee.com/ajream/images/raw/master/img/2021-08-0113-56-04_image-20210801135559755.png)\n\n使用注解开发,要导入context约束,提供注解的支持\n\n```xml\n\n\n\n \n\n \n \n\n```\n\n\n\n1. bean\n\n ```java\n @component:\n //组件,在创建的类前面加上这个注解,说明这个类已经被spring管理了,相当于:\n //\n //没有参数时,只能以getBean(\"类名的小写字母\") 来获取bean\n @component(value=\"xxx\")\n //有参数时,可以用getBean(\"xxx\") 来获取bean\n ```\n\n Person类:\n\n ```java\n package com.ajream.pojo;\n \n import org.springframework.stereotype.Component;\n \n @Component\t\t\t\t//没有参数时,只能以getBean(\"person\") 来获取bean\n public class Person {\n public String name = \"张三\";\n }\n \n ```\n\n \n\n2. 属性注入\n\n\t ```java\n @Value(\"李四\"):\n //相当于\n ```\n\n \n\n ```java\n \n @Component\n public class Person {\n \n @Value(\"李四\")\t\t//注入属性值\n public String name;\n }\n ```\n\n 也可以在setter方法前使用:\n\n ```java\n @Component\n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n \n\n4. 衍生注解\n\n @Component有几个衍生注解,在web中一般按照mvc三层架构划分:\n\n - dao层:@Repository\n - service层:@Service\n - Controller层:@Controller\n\n 这4个注解功能一样的\n\n5. 作用域scope\n\n 单例\n\n ```java\n @Component\n @Scope(value = \"singleton\") \n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n 原型\n\n ```java\n @Scope(value = \"prototype\") \n ```\n\n6. 小结:\n\n xml与注解\n\n - xml:万能\n\n - 注解:不是自己的类用不了,维护相对复杂\n\n xml与注解最佳实践:\n\n - xml用来管理bean\n\n - 注解只负责属性注入\n\n - 注意:要让注解生效,必须开启注解的支持\n\n ```xml\n \n \n ```\n\n \n\n## 使用Java的方式配置spring\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master4/)\n\n在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置\n\n用MyConfig类来代替beans.xml:\n\n```java\npackage com.ajream.config;\n\nimport com.ajream.pojo.Person;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class MyConfig {\n\n @Bean\n //相当于\n public Person getPerson1(){ //方法名就是bean的id\n return new Person();\n }\n}\n\n```\n\nPerson类:\n\n```java\npackage com.ajream.pojo;\n\nimport org.springframework.beans.factory.annotation.Value;\n\npublic class Person {\n public String name;\n\n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n}\n\n```\n\nMyTest类\n\n\n\n![image-20210801151413775](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-14-18_image-20210801151413775.png)\n\n\n\n这里不使用xml配置文件了,使用Java配置类 `MyConfig`\n\n```java\nimport com.ajream.config.MyConfig;\nimport com.ajream.pojo.Person;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\n\n\npublic class MyTest {\n\n @Test\n public void test1() {\n //加载配置类\n ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);\n\n Person p = context.getBean(\"getPerson1\", Person.class);\n\n System.out.println(p.getName());\n\n }\n}\n\n```\n\n\n\n由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:\n\n![image-20210801155244594](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-52-49_image-20210801155244594.png)","source":"_posts/Spring/11-使用注解开发.md","raw":"---\ntitle: Spring(11)-使用注解开发\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 38b3e585\ndate: 2021-07-28 20:55:13\ndescription: spring使用注解开发,一种还要依赖xml配置文件,另一种可以完全抛弃xml配置文件,使用纯Java来完成属性注入\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 使用注解开发\n\n## 依然使用xml配置文件来管理\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master3/)\n\nspring4之后,要使用注解开发,必须保证aop的包导入了\n\n![image-20210801135559755](https://gitee.com/ajream/images/raw/master/img/2021-08-0113-56-04_image-20210801135559755.png)\n\n使用注解开发,要导入context约束,提供注解的支持\n\n```xml\n\n\n\n \n\n \n \n\n```\n\n\n\n1. bean\n\n ```java\n @component:\n //组件,在创建的类前面加上这个注解,说明这个类已经被spring管理了,相当于:\n //\n //没有参数时,只能以getBean(\"类名的小写字母\") 来获取bean\n @component(value=\"xxx\")\n //有参数时,可以用getBean(\"xxx\") 来获取bean\n ```\n\n Person类:\n\n ```java\n package com.ajream.pojo;\n \n import org.springframework.stereotype.Component;\n \n @Component\t\t\t\t//没有参数时,只能以getBean(\"person\") 来获取bean\n public class Person {\n public String name = \"张三\";\n }\n \n ```\n\n \n\n2. 属性注入\n\n\t ```java\n @Value(\"李四\"):\n //相当于\n ```\n\n \n\n ```java\n \n @Component\n public class Person {\n \n @Value(\"李四\")\t\t//注入属性值\n public String name;\n }\n ```\n\n 也可以在setter方法前使用:\n\n ```java\n @Component\n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n \n\n4. 衍生注解\n\n @Component有几个衍生注解,在web中一般按照mvc三层架构划分:\n\n - dao层:@Repository\n - service层:@Service\n - Controller层:@Controller\n\n 这4个注解功能一样的\n\n5. 作用域scope\n\n 单例\n\n ```java\n @Component\n @Scope(value = \"singleton\") \n public class Person {\n public String name;\n \n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n }\n ```\n\n 原型\n\n ```java\n @Scope(value = \"prototype\") \n ```\n\n6. 小结:\n\n xml与注解\n\n - xml:万能\n\n - 注解:不是自己的类用不了,维护相对复杂\n\n xml与注解最佳实践:\n\n - xml用来管理bean\n\n - 注解只负责属性注入\n\n - 注意:要让注解生效,必须开启注解的支持\n\n ```xml\n \n \n ```\n\n \n\n## 使用Java的方式配置spring\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master4/)\n\n在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置\n\n用MyConfig类来代替beans.xml:\n\n```java\npackage com.ajream.config;\n\nimport com.ajream.pojo.Person;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class MyConfig {\n\n @Bean\n //相当于\n public Person getPerson1(){ //方法名就是bean的id\n return new Person();\n }\n}\n\n```\n\nPerson类:\n\n```java\npackage com.ajream.pojo;\n\nimport org.springframework.beans.factory.annotation.Value;\n\npublic class Person {\n public String name;\n\n @Value(\"李四\")\n public void setName(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n}\n\n```\n\nMyTest类\n\n\n\n![image-20210801151413775](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-14-18_image-20210801151413775.png)\n\n\n\n这里不使用xml配置文件了,使用Java配置类 `MyConfig`\n\n```java\nimport com.ajream.config.MyConfig;\nimport com.ajream.pojo.Person;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.AnnotationConfigApplicationContext;\n\n\npublic class MyTest {\n\n @Test\n public void test1() {\n //加载配置类\n ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);\n\n Person p = context.getBean(\"getPerson1\", Person.class);\n\n System.out.println(p.getName());\n\n }\n}\n\n```\n\n\n\n由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:\n\n![image-20210801155244594](https://gitee.com/ajream/images/raw/master/img/2021-08-0115-52-49_image-20210801155244594.png)","slug":"Spring/11-使用注解开发","published":1,"updated":"2021-09-10T03:16:58.357Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p8002cakvedimceatv","content":"

使用注解开发

依然使用xml配置文件来管理

项目地址

\n

spring4之后,要使用注解开发,必须保证aop的包导入了

\n

\"image-20210801135559755\"

\n

使用注解开发,要导入context约束,提供注解的支持

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>
<!-- 指定要扫描的包,即在该包下的注解生效-->
<context:component-scan base-package="com.ajream.pojo"/>

</beans>
\n
    \n
  1. bean

    \n
    1
    2
    3
    4
    5
    6
    @component
    //组件,在创建的类前面加上这个注解,说明这个类已经被spring管理了,相当于:
    //<bean id="person" class="com.ajream.pojo.Person">
    //没有参数时,只能以getBean("类名的小写字母") 来获取bean
    @component(value="xxx")
    //有参数时,可以用getBean("xxx") 来获取bean
    \n

    Person类:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.ajream.pojo;

    import org.springframework.stereotype.Component;

    @Component\t\t\t\t//没有参数时,只能以getBean("person") 来获取bean
    public class Person {
    public String name = "张三";
    }

    \n
  2. \n
\n
    \n
  1. 属性注入

    \n
    1
    2
    @Value("李四")
    //相当于<property name="name" value="李四" />
    \n
  2. \n
\n
1
2
3
4
5
6
7

@Component
public class Person {

@Value("李四")\t\t//注入属性值
public String name;
}
\n

也可以在setter方法前使用:

\n
1
2
3
4
5
6
7
8
9
@Component
public class Person {
public String name;

@Value("李四")
public void setName(String name) {
this.name = name;
}
}
\n
    \n
  1. 衍生注解

    \n

    @Component有几个衍生注解,在web中一般按照mvc三层架构划分:

    \n
      \n
    • dao层:@Repository
    • \n
    • service层:@Service
    • \n
    • Controller层:@Controller
    • \n
    \n

    这4个注解功能一样的

    \n
  2. \n
  3. 作用域scope

    \n

    单例

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Component
    @Scope(value = "singleton")
    public class Person {
    public String name;

    @Value("李四")
    public void setName(String name) {
    this.name = name;
    }
    }
    \n

    原型

    \n
    1
    @Scope(value = "prototype")  
    \n
  4. \n
  5. 小结:

    \n

    xml与注解

    \n
      \n
    • xml:万能

      \n
    • \n
    • 注解:不是自己的类用不了,维护相对复杂

      \n
    • \n
    \n

    xml与注解最佳实践:

    \n
      \n
    • xml用来管理bean

      \n
    • \n
    • 注解只负责属性注入

      \n
    • \n
    • 注意:要让注解生效,必须开启注解的支持

      \n
      1
      2
      <context:annotation-config/>
      <context:component-scan base-package="com.ajream.pojo"/>
      \n
    • \n
    \n
  6. \n
\n

使用Java的方式配置spring

项目地址

\n

在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置

\n

用MyConfig类来代替beans.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.config;

import com.ajream.pojo.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

@Bean
//相当于<bean id="getPerson1" class="com.ajream.pojo.Person">
public Person getPerson1(){ //方法名就是bean的id
return new Person();
}
}

\n

Person类:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.pojo;

import org.springframework.beans.factory.annotation.Value;

public class Person {
public String name;

@Value("李四")
public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

\n

MyTest类

\n

\"image-20210801151413775\"

\n

这里不使用xml配置文件了,使用Java配置类 MyConfig

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.ajream.config.MyConfig;
import com.ajream.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class MyTest {

@Test
public void test1() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

Person p = context.getBean("getPerson1", Person.class);

System.out.println(p.getName());

}
}

\n

由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:

\n

\"image-20210801155244594\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

使用注解开发

依然使用xml配置文件来管理

项目地址

\n

spring4之后,要使用注解开发,必须保证aop的包导入了

\n

\"image-20210801135559755\"

\n

使用注解开发,要导入context约束,提供注解的支持

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>
<!-- 指定要扫描的包,即在该包下的注解生效-->
<context:component-scan base-package="com.ajream.pojo"/>

</beans>
\n
    \n
  1. bean

    \n
    1
    2
    3
    4
    5
    6
    @component
    //组件,在创建的类前面加上这个注解,说明这个类已经被spring管理了,相当于:
    //<bean id="person" class="com.ajream.pojo.Person">
    //没有参数时,只能以getBean("类名的小写字母") 来获取bean
    @component(value="xxx")
    //有参数时,可以用getBean("xxx") 来获取bean
    \n

    Person类:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.ajream.pojo;

    import org.springframework.stereotype.Component;

    @Component\t\t\t\t//没有参数时,只能以getBean("person") 来获取bean
    public class Person {
    public String name = "张三";
    }

    \n
  2. \n
\n
    \n
  1. 属性注入

    \n
    1
    2
    @Value("李四")
    //相当于<property name="name" value="李四" />
    \n
  2. \n
\n
1
2
3
4
5
6
7

@Component
public class Person {

@Value("李四")\t\t//注入属性值
public String name;
}
\n

也可以在setter方法前使用:

\n
1
2
3
4
5
6
7
8
9
@Component
public class Person {
public String name;

@Value("李四")
public void setName(String name) {
this.name = name;
}
}
\n
    \n
  1. 衍生注解

    \n

    @Component有几个衍生注解,在web中一般按照mvc三层架构划分:

    \n
      \n
    • dao层:@Repository
    • \n
    • service层:@Service
    • \n
    • Controller层:@Controller
    • \n
    \n

    这4个注解功能一样的

    \n
  2. \n
  3. 作用域scope

    \n

    单例

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Component
    @Scope(value = "singleton")
    public class Person {
    public String name;

    @Value("李四")
    public void setName(String name) {
    this.name = name;
    }
    }
    \n

    原型

    \n
    1
    @Scope(value = "prototype")  
    \n
  4. \n
  5. 小结:

    \n

    xml与注解

    \n
      \n
    • xml:万能

      \n
    • \n
    • 注解:不是自己的类用不了,维护相对复杂

      \n
    • \n
    \n

    xml与注解最佳实践:

    \n
      \n
    • xml用来管理bean

      \n
    • \n
    • 注解只负责属性注入

      \n
    • \n
    • 注意:要让注解生效,必须开启注解的支持

      \n
      1
      2
      <context:annotation-config/>
      <context:component-scan base-package="com.ajream.pojo"/>
      \n
    • \n
    \n
  6. \n
\n

使用Java的方式配置spring

项目地址

\n

在此前一直都使用beans.xml配置文件来配置spring如属性注入,现在可以不使用xml了,将spring配置全权交由Java来配置

\n

用MyConfig类来代替beans.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.config;

import com.ajream.pojo.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

@Bean
//相当于<bean id="getPerson1" class="com.ajream.pojo.Person">
public Person getPerson1(){ //方法名就是bean的id
return new Person();
}
}

\n

Person类:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.pojo;

import org.springframework.beans.factory.annotation.Value;

public class Person {
public String name;

@Value("李四")
public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

\n

MyTest类

\n

\"image-20210801151413775\"

\n

这里不使用xml配置文件了,使用Java配置类 MyConfig

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.ajream.config.MyConfig;
import com.ajream.pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class MyTest {

@Test
public void test1() {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

Person p = context.getBean("getPerson1", Person.class);

System.out.println(p.getName());

}
}

\n

由于MyConfig类也是配置类,所以它具有beans.xml的功能,比如:

\n

\"image-20210801155244594\"

\n"},{"title":"Spring(13)-动态代理","abbrlink":"26c04051","date":"2021-07-29T07:53:34.000Z","description":"Java动态代理模式举例介绍","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 动态代理\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n底层用到`反射`\n\n- 动态代理与静态代理角色一样\n- 动态代理的代理类是动态生成,并非我们直接写好\n- 分类:基于接口和基于类的\n - 基于接口——JDK动态代理\n - 基于类——cglib\n - Java字节码实现——javasist\n\n\n\n需要了解2个类:\n\n- Proxy:调用`newProxyInstance` 方法用于生成代理角色\n\n- InvocationHandler:重写 `invoke` 方法,指明代理角色要处理的功能,详细[查看项目](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n ```java\n //这是一个用于创建代理角色的类,不是代理类\n public class ProxyInvocationHandler implements InvocationHandler {\n \n //指向被代理的接口(即真实角色)\n private Rent r;\n \n public void setRent(Rent r) {\n this.r = r;\n }\n \n //生成代理角色\n public Object getProxy(){\n return Proxy.newProxyInstance(this.getClass().getClassLoader(), r.getClass().getInterfaces(), this);\n }\n \n @Override\n //处理代理实例(即被代理者),并返回结果\n public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n seeHouse();\n fare();\n Object result = method.invoke(r, args);\n return result;\n }\n \n ```\n\n \n\n\n\n动态代理好处:\n\n- 可以使真实角色的操作更加纯粹\n- 公共交给代理角色,实现业务分工\n- 公共业务扩展时,方便集中管理\n- 一个动态代理类可以代理多个类(只要实现的是同一个接口即可)\n\n\n\n","source":"_posts/Spring/13-动态代理.md","raw":"---\ntitle: Spring(13)-动态代理\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 26c04051\ndate: 2021-07-29 15:53:34\ndescription: Java动态代理模式举例介绍\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 动态代理\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n底层用到`反射`\n\n- 动态代理与静态代理角色一样\n- 动态代理的代理类是动态生成,并非我们直接写好\n- 分类:基于接口和基于类的\n - 基于接口——JDK动态代理\n - 基于类——cglib\n - Java字节码实现——javasist\n\n\n\n需要了解2个类:\n\n- Proxy:调用`newProxyInstance` 方法用于生成代理角色\n\n- InvocationHandler:重写 `invoke` 方法,指明代理角色要处理的功能,详细[查看项目](https://codechina.csdn.net/m0_46079750/spring1-proxy/-/tree/master3)\n\n ```java\n //这是一个用于创建代理角色的类,不是代理类\n public class ProxyInvocationHandler implements InvocationHandler {\n \n //指向被代理的接口(即真实角色)\n private Rent r;\n \n public void setRent(Rent r) {\n this.r = r;\n }\n \n //生成代理角色\n public Object getProxy(){\n return Proxy.newProxyInstance(this.getClass().getClassLoader(), r.getClass().getInterfaces(), this);\n }\n \n @Override\n //处理代理实例(即被代理者),并返回结果\n public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n seeHouse();\n fare();\n Object result = method.invoke(r, args);\n return result;\n }\n \n ```\n\n \n\n\n\n动态代理好处:\n\n- 可以使真实角色的操作更加纯粹\n- 公共交给代理角色,实现业务分工\n- 公共业务扩展时,方便集中管理\n- 一个动态代理类可以代理多个类(只要实现的是同一个接口即可)\n\n\n\n","slug":"Spring/13-动态代理","published":1,"updated":"2021-09-10T03:16:40.037Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6p9002fakve7ps4flkp","content":"

动态代理

项目地址

\n

底层用到反射

\n
    \n
  • 动态代理与静态代理角色一样
  • \n
  • 动态代理的代理类是动态生成,并非我们直接写好
  • \n
  • 分类:基于接口和基于类的
      \n
    • 基于接口——JDK动态代理
    • \n
    • 基于类——cglib
    • \n
    • Java字节码实现——javasist
    • \n
    \n
  • \n
\n

需要了解2个类:

\n
    \n
  • Proxy:调用newProxyInstance 方法用于生成代理角色

    \n
  • \n
  • InvocationHandler:重写 invoke 方法,指明代理角色要处理的功能,详细查看项目

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //这是一个用于创建代理角色的类,不是代理类
    public class ProxyInvocationHandler implements InvocationHandler {

    //指向被代理的接口(即真实角色)
    private Rent r;

    public void setRent(Rent r) {
    this.r = r;
    }

    //生成代理角色
    public Object getProxy(){
    return Proxy.newProxyInstance(this.getClass().getClassLoader(), r.getClass().getInterfaces(), this);
    }

    @Override
    //处理代理实例(即被代理者),并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    seeHouse();
    fare();
    Object result = method.invoke(r, args);
    return result;
    }

    \n
  • \n
\n

动态代理好处:

\n
    \n
  • 可以使真实角色的操作更加纯粹
  • \n
  • 公共交给代理角色,实现业务分工
  • \n
  • 公共业务扩展时,方便集中管理
  • \n
  • 一个动态代理类可以代理多个类(只要实现的是同一个接口即可)
  • \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

动态代理

项目地址

\n

底层用到反射

\n
    \n
  • 动态代理与静态代理角色一样
  • \n
  • 动态代理的代理类是动态生成,并非我们直接写好
  • \n
  • 分类:基于接口和基于类的
      \n
    • 基于接口——JDK动态代理
    • \n
    • 基于类——cglib
    • \n
    • Java字节码实现——javasist
    • \n
    \n
  • \n
\n

需要了解2个类:

\n
    \n
  • Proxy:调用newProxyInstance 方法用于生成代理角色

    \n
  • \n
  • InvocationHandler:重写 invoke 方法,指明代理角色要处理的功能,详细查看项目

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //这是一个用于创建代理角色的类,不是代理类
    public class ProxyInvocationHandler implements InvocationHandler {

    //指向被代理的接口(即真实角色)
    private Rent r;

    public void setRent(Rent r) {
    this.r = r;
    }

    //生成代理角色
    public Object getProxy(){
    return Proxy.newProxyInstance(this.getClass().getClassLoader(), r.getClass().getInterfaces(), this);
    }

    @Override
    //处理代理实例(即被代理者),并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    seeHouse();
    fare();
    Object result = method.invoke(r, args);
    return result;
    }

    \n
  • \n
\n

动态代理好处:

\n
    \n
  • 可以使真实角色的操作更加纯粹
  • \n
  • 公共交给代理角色,实现业务分工
  • \n
  • 公共业务扩展时,方便集中管理
  • \n
  • 一个动态代理类可以代理多个类(只要实现的是同一个接口即可)
  • \n
\n"},{"title":"Spring(14)-springAOP","abbrlink":"a1a1eb5","date":"2021-07-30T05:50:41.000Z","description":"springAOP使用了动态代理模式,其基本使用进入文章详细了解","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# Spring Aop\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master)\n\n## aop实现方式1(实现spring接口)\n\n### 导包\n\n使用之前要导入依赖包\n\n![image-20210803155603975](https://gitee.com/ajream/images/raw/master/img/2021-08-0315-56-08_image-20210803155603975.png)\n\n```xml\n\n \n org.springframework\n spring-context\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.12\n \n \n org.aspectj\n aspectjweaver\n 1.9.6\n \n\n```\n\n\n\n项目结构:\n\n```\nspring1-aop\n├───.idea\n├───src\n│ ├───main\n│ │ ├───java\n│ │ │ └───com\n│ │ │ └───ajream\n│ │ │ ├───log\n│ │ │ └───service\n│ │ └───resources\n│ └───test\n └───java\n```\n\n\n\n### 业务模块service\n\n被代理的接口,包含了要实现的具体功能\n\nUserService.java接口:\n\n```java\npackage com.ajream.service;\n\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n```\n\nUserServiceImpl.java\n\n```java\npackage com.ajream.service;\n\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n### 代理模块\n\n在原有基础上扩展一些功能\n\nLog.java (在实现某个业务(调用某个方法)前,打印一些相关信息)\n\n> 说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.MethodBeforeAdvice;\n\nimport java.lang.reflect.Method;\n\npublic class Log implements MethodBeforeAdvice {\n @Override\n public void before(Method method, Object[] args, Object target) throws Throwable {\n assert target != null;\n System.out.println(target.getClass().getName() + \"的\" + method.getName() + \"方法被执行了\");\n }\n}\n\n```\n\n\n\nAfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息) \n\n> 说明:重写AfterReturningAdvice的 `afterReturning` 方法,说明在切入点之后执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.AfterReturningAdvice;\nimport java.lang.reflect.Method;\n\npublic class AfterLog implements AfterReturningAdvice {\n @Override\n public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {\n System.out.println(\"执行了\" +method.getName()+ \"方法,返回值为:\"+returnValue);\n }\n}\n\n```\n\n\n\nApplicationContext.xml 配置文件\n\n```xml\n\n\n\n \n \n \n\n \n\n \n\n\n \n \n\n \n\n\n```\n\n> 说明:\n>\n> 1. 注意添加aop支持\n>\n> ```\n> xmlns:aop=\"http://www.springframework.org/schema/aop\"\n> http://www.springframework.org/schema/aop\n> https://www.springframework.org/schema/aop/spring-aop.xsd\n> ```\n>\n> 2. aop配置(使用原生spring api接口)\n>\n> ```xml\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n### 测试\n\nMyTest.java测试\n\n```java\nimport com.ajream.service.UserService;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n @Test\n public void test(){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"ApplicationContext.xml\");\n\n //动态代理的是接口\n UserService userService = context.getBean(\"userService\", UserService.class);\n userService.add();\n }\n}\n\n```\n\n\n\n测试结果:\n\n![image-20210808163457649](https://gitee.com/ajream/images/raw/master/img/2021-08-0816-35-03_image-20210808163457649.png)\n\n\n\n## Aop实现方式2(自定义切入类)\n\n主要是aop配置方式不同\n\n之前是使用原生spring api方式,分别实现了`MethodBeforeAdvice`和`AfterReturningAdvice`这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:\n\n```xml\n\n \n \n \n \n \n \n \n\n```\n\n---\n\n现在改用自定义切入面方式:\n\n首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)\n\n\n\n```java\npackage com.ajream.diy;\n\n//自定义切入面\npublic class DiyPointCut {\n public void before(){\n System.out.println(\"=====方法执行前======\");\n }\n\n public void after(){\n System.out.println(\"======方法执行后======\");\n }\n}\n\n```\n\naop配置:\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n## aop实现方式3(使用注解开发)\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master1)\n\n首先开启aop注解支持\n\n```xml\n\n\n\n \n\n\n \n\n \n\n```\n\n\n\n添加AnnotationPointCut.java类\n\n```java\npackage com.ajream.diy;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.After;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\n\n@Aspect //标注这是一个切面\npublic class AnnotationPointCut {\n\n @Before(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //切入位置\n public void before(){\n System.out.println(\"=====方法执行前=====\");\n }\n\n @After(\"execution(* com.ajream.service.UserServiceImpl.*(..))\")\n public void after(){\n System.out.println(\"=====方法执行后=====\");\n }\n\n @Around(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //环绕增强\n public void around(ProceedingJoinPoint jp) throws Throwable {\n System.out.println(\"环绕前\");\n Object proceed = jp.proceed();// 执行方法\n System.out.println(\"执行的方法签名:\" + jp.getSignature());\n System.out.println(\"环绕后\");\n\n }\n}\n\n```\n\n> 注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 `@Before` `@After` 这2个注解的内容,所以输出结果如下:\n>\n> ![image-20210808173720981](https://gitee.com/ajream/images/raw/master/img/2021-08-08 17-37-27_image-20210808173720981.png)\n\n","source":"_posts/Spring/14-springAOP.md","raw":"---\ntitle: Spring(14)-springAOP\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: a1a1eb5\ndate: 2021-07-30 13:50:41\ndescription: springAOP使用了动态代理模式,其基本使用进入文章详细了解\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# Spring Aop\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master)\n\n## aop实现方式1(实现spring接口)\n\n### 导包\n\n使用之前要导入依赖包\n\n![image-20210803155603975](https://gitee.com/ajream/images/raw/master/img/2021-08-0315-56-08_image-20210803155603975.png)\n\n```xml\n\n \n org.springframework\n spring-context\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.12\n \n \n org.aspectj\n aspectjweaver\n 1.9.6\n \n\n```\n\n\n\n项目结构:\n\n```\nspring1-aop\n├───.idea\n├───src\n│ ├───main\n│ │ ├───java\n│ │ │ └───com\n│ │ │ └───ajream\n│ │ │ ├───log\n│ │ │ └───service\n│ │ └───resources\n│ └───test\n └───java\n```\n\n\n\n### 业务模块service\n\n被代理的接口,包含了要实现的具体功能\n\nUserService.java接口:\n\n```java\npackage com.ajream.service;\n\npublic interface UserService {\n void add();\n void delete();\n void update();\n void query();\n}\n```\n\nUserServiceImpl.java\n\n```java\npackage com.ajream.service;\n\npublic class UserServiceImpl implements UserService {\n @Override\n public void add() {\n System.out.println(\"增加一个用户\");\n }\n\n @Override\n public void delete() {\n System.out.println(\"删除一个用户\");\n }\n\n @Override\n public void update() {\n System.out.println(\"修改一个用户\");\n }\n\n @Override\n public void query() {\n System.out.println(\"查询一个用户\");\n }\n}\n\n```\n\n\n\n### 代理模块\n\n在原有基础上扩展一些功能\n\nLog.java (在实现某个业务(调用某个方法)前,打印一些相关信息)\n\n> 说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.MethodBeforeAdvice;\n\nimport java.lang.reflect.Method;\n\npublic class Log implements MethodBeforeAdvice {\n @Override\n public void before(Method method, Object[] args, Object target) throws Throwable {\n assert target != null;\n System.out.println(target.getClass().getName() + \"的\" + method.getName() + \"方法被执行了\");\n }\n}\n\n```\n\n\n\nAfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息) \n\n> 说明:重写AfterReturningAdvice的 `afterReturning` 方法,说明在切入点之后执行这个方法\n\n```java\npackage com.ajream.log;\n\nimport org.springframework.aop.AfterReturningAdvice;\nimport java.lang.reflect.Method;\n\npublic class AfterLog implements AfterReturningAdvice {\n @Override\n public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {\n System.out.println(\"执行了\" +method.getName()+ \"方法,返回值为:\"+returnValue);\n }\n}\n\n```\n\n\n\nApplicationContext.xml 配置文件\n\n```xml\n\n\n\n \n \n \n\n \n\n \n\n\n \n \n\n \n\n\n```\n\n> 说明:\n>\n> 1. 注意添加aop支持\n>\n> ```\n> xmlns:aop=\"http://www.springframework.org/schema/aop\"\n> http://www.springframework.org/schema/aop\n> https://www.springframework.org/schema/aop/spring-aop.xsd\n> ```\n>\n> 2. aop配置(使用原生spring api接口)\n>\n> ```xml\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n### 测试\n\nMyTest.java测试\n\n```java\nimport com.ajream.service.UserService;\nimport org.junit.Test;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n @Test\n public void test(){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"ApplicationContext.xml\");\n\n //动态代理的是接口\n UserService userService = context.getBean(\"userService\", UserService.class);\n userService.add();\n }\n}\n\n```\n\n\n\n测试结果:\n\n![image-20210808163457649](https://gitee.com/ajream/images/raw/master/img/2021-08-0816-35-03_image-20210808163457649.png)\n\n\n\n## Aop实现方式2(自定义切入类)\n\n主要是aop配置方式不同\n\n之前是使用原生spring api方式,分别实现了`MethodBeforeAdvice`和`AfterReturningAdvice`这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:\n\n```xml\n\n \n \n \n \n \n \n \n\n```\n\n---\n\n现在改用自定义切入面方式:\n\n首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)\n\n\n\n```java\npackage com.ajream.diy;\n\n//自定义切入面\npublic class DiyPointCut {\n public void before(){\n System.out.println(\"=====方法执行前======\");\n }\n\n public void after(){\n System.out.println(\"======方法执行后======\");\n }\n}\n\n```\n\naop配置:\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n## aop实现方式3(使用注解开发)\n\n\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring1-aop/-/tree/master1)\n\n首先开启aop注解支持\n\n```xml\n\n\n\n \n\n\n \n\n \n\n```\n\n\n\n添加AnnotationPointCut.java类\n\n```java\npackage com.ajream.diy;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.After;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\n\n@Aspect //标注这是一个切面\npublic class AnnotationPointCut {\n\n @Before(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //切入位置\n public void before(){\n System.out.println(\"=====方法执行前=====\");\n }\n\n @After(\"execution(* com.ajream.service.UserServiceImpl.*(..))\")\n public void after(){\n System.out.println(\"=====方法执行后=====\");\n }\n\n @Around(\"execution(* com.ajream.service.UserServiceImpl.*(..))\") //环绕增强\n public void around(ProceedingJoinPoint jp) throws Throwable {\n System.out.println(\"环绕前\");\n Object proceed = jp.proceed();// 执行方法\n System.out.println(\"执行的方法签名:\" + jp.getSignature());\n System.out.println(\"环绕后\");\n\n }\n}\n\n```\n\n> 注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 `@Before` `@After` 这2个注解的内容,所以输出结果如下:\n>\n> ![image-20210808173720981](https://gitee.com/ajream/images/raw/master/img/2021-08-08 17-37-27_image-20210808173720981.png)\n\n","slug":"Spring/14-springAOP","published":1,"updated":"2021-09-10T03:16:18.937Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pb002jakvefzhpc3tk","content":"

Spring Aop

项目地址

\n

aop实现方式1(实现spring接口)

导包

使用之前要导入依赖包

\n

\"image-20210803155603975\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
\n

项目结构:

\n
1
2
3
4
5
6
7
8
9
10
11
12
spring1-aop
├───.idea
├───src
│ ├───main
│ │ ├───java
│ │ │ └───com
│ │ │ └───ajream
│ │ │ ├───log
│ │ │ └───service
│ │ └───resources
│ └───test
└───java
\n

业务模块service

被代理的接口,包含了要实现的具体功能

\n

UserService.java接口:

\n
1
2
3
4
5
6
7
8
package com.ajream.service;

public interface UserService {
void add();
void delete();
void update();
void query();
}
\n

UserServiceImpl.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ajream.service;

public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加一个用户");
}

@Override
public void delete() {
System.out.println("删除一个用户");
}

@Override
public void update() {
System.out.println("修改一个用户");
}

@Override
public void query() {
System.out.println("查询一个用户");
}
}

\n

代理模块

在原有基础上扩展一些功能

\n

Log.java (在实现某个业务(调用某个方法)前,打印一些相关信息)

\n
\n

说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.ajream.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
assert target != null;
System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}

\n

AfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息)

\n
\n

说明:重写AfterReturningAdvice的 afterReturning 方法,说明在切入点之后执行这个方法

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
package com.ajream.log;

import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" +method.getName()+ "方法,返回值为:"+returnValue);
}
}

\n

ApplicationContext.xml 配置文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
>

<bean id="userService" class="com.ajream.service.UserServiceImpl" />
<bean id="log" class="com.ajream.log.Log" />
<bean id="afterLog" class="com.ajream.log.AfterLog" />

<aop:config>
<!-- 定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
<aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

<!-- 执行环绕增加-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
<aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

</aop:config>

</beans>
\n
\n

说明:

\n
    \n
  1. 注意添加aop支持

    \n
    1
    2
    3
    xmlns:aop="http://www.springframework.org/schema/aop"
    http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd
    \n
  2. \n
  3. aop配置(使用原生spring api接口)

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <aop:config>
    <!--定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
    <aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

    <!--执行环绕增加,即在切入点前后执行下面的操作-->
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
    <aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

    </aop:config>
    \n
  4. \n
\n
\n

测试

MyTest.java测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.ajream.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");

//动态代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}

\n

测试结果:

\n

\"image-20210808163457649\"

\n

Aop实现方式2(自定义切入类)

主要是aop配置方式不同

\n

之前是使用原生spring api方式,分别实现了MethodBeforeAdviceAfterReturningAdvice这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:

\n
1
2
3
4
5
6
7
8
9
<aop:config>
<!--定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
<aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

<!--执行环绕增加,即在切入点前后执行下面的操作-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
<aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

</aop:config>
\n
\n

现在改用自定义切入面方式:

\n

首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ajream.diy;

//自定义切入面
public class DiyPointCut {
public void before(){
System.out.println("=====方法执行前======");
}

public void after(){
System.out.println("======方法执行后======");
}
}

\n

aop配置:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<!--方式2:自定义类-->
<bean id="diy" class="com.ajream.diy.DiyPointCut" />

<aop:config>
<!--定义切面,ref为要引用的类-->
<aop:aspect ref="diy" >
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>
<!-- aop:before表示在切入点point之前执行切面的before方法-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point" />
</aop:aspect>
</aop:config>
\n

aop实现方式3(使用注解开发)

项目地址

\n

首先开启aop注解支持

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
>

<bean id="userService" class="com.ajream.service.UserServiceImpl" />

<!--方式3,注解方式-->
<bean id="annotationPointCut" class="com.ajream.diy.AnnotationPointCut" />
<!--开启aop注解支持-->
<aop:aspectj-autoproxy />
</beans>
\n

添加AnnotationPointCut.java类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.ajream.diy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect //标注这是一个切面
public class AnnotationPointCut {

@Before("execution(* com.ajream.service.UserServiceImpl.*(..))") //切入位置
public void before(){
System.out.println("=====方法执行前=====");
}

@After("execution(* com.ajream.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====方法执行后=====");
}

@Around("execution(* com.ajream.service.UserServiceImpl.*(..))") //环绕增强
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Object proceed = jp.proceed();// 执行方法
System.out.println("执行的方法签名:" + jp.getSignature());
System.out.println("环绕后");

}
}

\n
\n

注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 @Before @After 这2个注解的内容,所以输出结果如下:

\n

\"image-20210808173720981\"

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Spring Aop

项目地址

\n

aop实现方式1(实现spring接口)

导包

使用之前要导入依赖包

\n

\"image-20210803155603975\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
\n

项目结构:

\n
1
2
3
4
5
6
7
8
9
10
11
12
spring1-aop
├───.idea
├───src
│ ├───main
│ │ ├───java
│ │ │ └───com
│ │ │ └───ajream
│ │ │ ├───log
│ │ │ └───service
│ │ └───resources
│ └───test
└───java
\n

业务模块service

被代理的接口,包含了要实现的具体功能

\n

UserService.java接口:

\n
1
2
3
4
5
6
7
8
package com.ajream.service;

public interface UserService {
void add();
void delete();
void update();
void query();
}
\n

UserServiceImpl.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ajream.service;

public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加一个用户");
}

@Override
public void delete() {
System.out.println("删除一个用户");
}

@Override
public void update() {
System.out.println("修改一个用户");
}

@Override
public void query() {
System.out.println("查询一个用户");
}
}

\n

代理模块

在原有基础上扩展一些功能

\n

Log.java (在实现某个业务(调用某个方法)前,打印一些相关信息)

\n
\n

说明:重写MethodBeforeAdvice接口的before方法,说明在切入点之前执行这个方法

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.ajream.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
assert target != null;
System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}

\n

AfterLog.java (在实现某个业务(调用某个方法)后,打印一些相关信息)

\n
\n

说明:重写AfterReturningAdvice的 afterReturning 方法,说明在切入点之后执行这个方法

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
package com.ajream.log;

import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" +method.getName()+ "方法,返回值为:"+returnValue);
}
}

\n

ApplicationContext.xml 配置文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
>

<bean id="userService" class="com.ajream.service.UserServiceImpl" />
<bean id="log" class="com.ajream.log.Log" />
<bean id="afterLog" class="com.ajream.log.AfterLog" />

<aop:config>
<!-- 定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
<aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

<!-- 执行环绕增加-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
<aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

</aop:config>

</beans>
\n
\n

说明:

\n
    \n
  1. 注意添加aop支持

    \n
    1
    2
    3
    xmlns:aop="http://www.springframework.org/schema/aop"
    http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd
    \n
  2. \n
  3. aop配置(使用原生spring api接口)

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <aop:config>
    <!--定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
    <aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

    <!--执行环绕增加,即在切入点前后执行下面的操作-->
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
    <aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

    </aop:config>
    \n
  4. \n
\n
\n

测试

MyTest.java测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.ajream.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");

//动态代理的是接口
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}
}

\n

测试结果:

\n

\"image-20210808163457649\"

\n

Aop实现方式2(自定义切入类)

主要是aop配置方式不同

\n

之前是使用原生spring api方式,分别实现了MethodBeforeAdviceAfterReturningAdvice这两个接口,因此在xml中只需要指明要切入哪个位置,执行什么操作,在执行目标接口的方法时spring就会根据xml配置自动去执行对应操作,如下:

\n
1
2
3
4
5
6
7
8
9
<aop:config>
<!--定义切入点pointcut, execution(返回类型 包名.类名.方法名(参数)) "*"表示任意 -->
<aop:pointcut id="pointCut" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>

<!--执行环绕增加,即在切入点前后执行下面的操作-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut" />
<aop:advisor advice-ref="log" pointcut-ref="pointCut"/>

</aop:config>
\n
\n

现在改用自定义切入面方式:

\n

首先添加一个切面DiyPointCut(即一个类,包含了要切入的方法)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ajream.diy;

//自定义切入面
public class DiyPointCut {
public void before(){
System.out.println("=====方法执行前======");
}

public void after(){
System.out.println("======方法执行后======");
}
}

\n

aop配置:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<!--方式2:自定义类-->
<bean id="diy" class="com.ajream.diy.DiyPointCut" />

<aop:config>
<!--定义切面,ref为要引用的类-->
<aop:aspect ref="diy" >
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.ajream.service.UserServiceImpl.*(..))"/>
<!-- aop:before表示在切入点point之前执行切面的before方法-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point" />
</aop:aspect>
</aop:config>
\n

aop实现方式3(使用注解开发)

项目地址

\n

首先开启aop注解支持

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
>

<bean id="userService" class="com.ajream.service.UserServiceImpl" />

<!--方式3,注解方式-->
<bean id="annotationPointCut" class="com.ajream.diy.AnnotationPointCut" />
<!--开启aop注解支持-->
<aop:aspectj-autoproxy />
</beans>
\n

添加AnnotationPointCut.java类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.ajream.diy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect //标注这是一个切面
public class AnnotationPointCut {

@Before("execution(* com.ajream.service.UserServiceImpl.*(..))") //切入位置
public void before(){
System.out.println("=====方法执行前=====");
}

@After("execution(* com.ajream.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====方法执行后=====");
}

@Around("execution(* com.ajream.service.UserServiceImpl.*(..))") //环绕增强
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Object proceed = jp.proceed();// 执行方法
System.out.println("执行的方法签名:" + jp.getSignature());
System.out.println("环绕后");

}
}

\n
\n

注意,如果添加了环绕增强,首先执行环绕增强,执行了proceed方法后才会触发 @Before @After 这2个注解的内容,所以输出结果如下:

\n

\"image-20210808173720981\"

\n
\n"},{"title":"Spring(2)-SpringIOC","abbrlink":"752d4bdb","date":"2021-07-24T07:57:27.000Z","description":"springIOC的结构与内容","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# SpringIOC\n\n\n\n![Spring+IOC](https://gitee.com/ajream/images/raw/master/img/2021-07-3021-11-35_Spring+IOC.svg)\n\n\n\n## IOC理论推导\n\n1. userDao接口\n\n userDaoImpl实现类(Mysql, Oracle, ...)\n\n2. userService业务接口\n\n userServiceImpl 业务实现\n\n\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n\n\n","source":"_posts/Spring/2-SpringIOC.md","raw":"---\ntitle: Spring(2)-SpringIOC\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 752d4bdb\ndate: 2021-07-24 15:57:27\ndescription: springIOC的结构与内容\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# SpringIOC\n\n\n\n![Spring+IOC](https://gitee.com/ajream/images/raw/master/img/2021-07-3021-11-35_Spring+IOC.svg)\n\n\n\n## IOC理论推导\n\n1. userDao接口\n\n userDaoImpl实现类(Mysql, Oracle, ...)\n\n2. userService业务接口\n\n userServiceImpl 业务实现\n\n\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n\n\n","slug":"Spring/2-SpringIOC","published":1,"updated":"2021-09-10T03:18:09.108Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pc002makvec2g3cfzf","content":"

SpringIOC

\"Spring+IOC\"

\n

IOC理论推导

    \n
  1. userDao接口

    \n

    userDaoImpl实现类(Mysql, Oracle, …)

    \n
  2. \n
  3. userService业务接口

    \n

    userServiceImpl 业务实现

    \n
  4. \n
\n

\"ioc\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

SpringIOC

\"Spring+IOC\"

\n

IOC理论推导

    \n
  1. userDao接口

    \n

    userDaoImpl实现类(Mysql, Oracle, …)

    \n
  2. \n
  3. userService业务接口

    \n

    userServiceImpl 业务实现

    \n
  4. \n
\n

\"ioc\"

\n"},{"title":"Spring(4)-HelloSpring程序","abbrlink":"59e405f3","date":"2021-07-25T04:48:31.000Z","description":"第一个spring程序","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n# HelloSpring项目\n\n\n\n## 使用maven创建\n\n\n\n创建后目录结构如下:\n\n![image-20210729125943211](https://gitee.com/ajream/images/raw/master/img/2021-07-2912-59-47_image-20210729125943211.png)\n\n\n\n\n\npom.xml:\n\n```xml\n\n\n 4.0.0\n\n org.ajream\n hello_spring\n 1.0-SNAPSHOT\n \n \n org.springframework\n spring-context\n 5.2.9.RELEASE\n test\n \n \n\n \n 8\n 8\n \n\n\n```\n\n\n\n## 代码\n\n1. 在java文件夹下创建`com.xxx.pojo.Hello`类\n\n![image-20210729130640647](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-06-43_image-20210729130640647.png)\n\n```java\npackage com.ajream.pojo;\n\npublic class Hello {\n private String str1; //先不管属性名为什么是str1\n\n public void setStr2(String s){ //先不管为什么函数名设置为setStr2\n this.str1 = s;\n }\n\n public void printStr(){\n System.out.println(\"Hello:->\" + str1);\n }\n}\n\n```\n\n\n\n2. 在resources下创建`beans.xml`配置文件\n\n```xml\n\n\n\n \n\n \n\n \n\n```\n\n> 该文件内容模板可以从spring[官网](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core)中找到\n>\n> ![image-20210729131316578](https://gitee.com/ajream/images/raw/master/img/2021-07-29 13-13-19_image-20210729131316578.png)\n>\n> ```xml\n> \n> xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n> xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n> https://www.springframework.org/schema/beans/spring-beans.xsd\">\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n3. 在test/java下创建MyTest类来进行测试输出:\n\n```java\nimport com.ajream.pojo.Hello;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n public static void main(String[] args){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\"); //加载xml配置文件\n Hello h = (Hello) context.getBean(\"helloworld\");\n h.printStr();\n }\n}\n\n```\n\n输出:\n\n![image-20210729131704285](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-17-08_image-20210729131704285.png)\n\n\n\n\n\n## 三者之间的关系\n\n![image-20210729134624753](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-46-29_image-20210729134624753.png)\n\n\n\n执行流程:\n\n1. 获取 `beans.xml` 配置文件信息\n2. 根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法\n3. 调用函数printStr\n\n\n\n\n\n","source":"_posts/Spring/4-HelloSpring程序.md","raw":"---\ntitle: Spring(4)-HelloSpring程序\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 59e405f3\ndate: 2021-07-25 12:48:31\ndescription: 第一个spring程序\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n# HelloSpring项目\n\n\n\n## 使用maven创建\n\n\n\n创建后目录结构如下:\n\n![image-20210729125943211](https://gitee.com/ajream/images/raw/master/img/2021-07-2912-59-47_image-20210729125943211.png)\n\n\n\n\n\npom.xml:\n\n```xml\n\n\n 4.0.0\n\n org.ajream\n hello_spring\n 1.0-SNAPSHOT\n \n \n org.springframework\n spring-context\n 5.2.9.RELEASE\n test\n \n \n\n \n 8\n 8\n \n\n\n```\n\n\n\n## 代码\n\n1. 在java文件夹下创建`com.xxx.pojo.Hello`类\n\n![image-20210729130640647](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-06-43_image-20210729130640647.png)\n\n```java\npackage com.ajream.pojo;\n\npublic class Hello {\n private String str1; //先不管属性名为什么是str1\n\n public void setStr2(String s){ //先不管为什么函数名设置为setStr2\n this.str1 = s;\n }\n\n public void printStr(){\n System.out.println(\"Hello:->\" + str1);\n }\n}\n\n```\n\n\n\n2. 在resources下创建`beans.xml`配置文件\n\n```xml\n\n\n\n \n\n \n\n \n\n```\n\n> 该文件内容模板可以从spring[官网](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core)中找到\n>\n> ![image-20210729131316578](https://gitee.com/ajream/images/raw/master/img/2021-07-29 13-13-19_image-20210729131316578.png)\n>\n> ```xml\n> \n> xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n> xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n> https://www.springframework.org/schema/beans/spring-beans.xsd\">\n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n3. 在test/java下创建MyTest类来进行测试输出:\n\n```java\nimport com.ajream.pojo.Hello;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\npublic class MyTest {\n public static void main(String[] args){\n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\"); //加载xml配置文件\n Hello h = (Hello) context.getBean(\"helloworld\");\n h.printStr();\n }\n}\n\n```\n\n输出:\n\n![image-20210729131704285](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-17-08_image-20210729131704285.png)\n\n\n\n\n\n## 三者之间的关系\n\n![image-20210729134624753](https://gitee.com/ajream/images/raw/master/img/2021-07-2913-46-29_image-20210729134624753.png)\n\n\n\n执行流程:\n\n1. 获取 `beans.xml` 配置文件信息\n2. 根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法\n3. 调用函数printStr\n\n\n\n\n\n","slug":"Spring/4-HelloSpring程序","published":1,"updated":"2021-09-10T03:18:01.840Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pd002qakve0ic15g6q","content":"

HelloSpring项目

使用maven创建

创建后目录结构如下:

\n

\"image-20210729125943211\"

\n

pom.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.ajream</groupId>
<artifactId>hello_spring</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

</project>
\n

代码

    \n
  1. 在java文件夹下创建com.xxx.pojo.Hello
  2. \n
\n

\"image-20210729130640647\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.ajream.pojo;

public class Hello {
private String str1; //先不管属性名为什么是str1

public void setStr2(String s){ //先不管为什么函数名设置为setStr2
this.str1 = s;
}

public void printStr(){
System.out.println("Hello:->" + str1);
}
}

\n
    \n
  1. 在resources下创建beans.xml配置文件
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="helloworld" class="com.ajream.pojo.Hello">
<!-- 通过name来识别出调用了哪个函数, value为传入参数-->
<property name="str2" value="spring" /> <!--这行代码表示调用函数 setStr2, 传入参数为字符串"spring" -->

</bean>
</beans>
\n
\n

该文件内容模板可以从spring官网中找到

\n

\"image-20210729131316578\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions go here -->

</beans>
\n
\n
    \n
  1. 在test/java下创建MyTest类来进行测试输出:
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import com.ajream.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //加载xml配置文件
Hello h = (Hello) context.getBean("helloworld");
h.printStr();
}
}

\n

输出:

\n

\"image-20210729131704285\"

\n

三者之间的关系

\"image-20210729134624753\"

\n

执行流程:

\n
    \n
  1. 获取 beans.xml 配置文件信息
  2. \n
  3. 根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法
  4. \n
  5. 调用函数printStr
  6. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

HelloSpring项目

使用maven创建

创建后目录结构如下:

\n

\"image-20210729125943211\"

\n

pom.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.ajream</groupId>
<artifactId>hello_spring</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

</project>
\n

代码

    \n
  1. 在java文件夹下创建com.xxx.pojo.Hello
  2. \n
\n

\"image-20210729130640647\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.ajream.pojo;

public class Hello {
private String str1; //先不管属性名为什么是str1

public void setStr2(String s){ //先不管为什么函数名设置为setStr2
this.str1 = s;
}

public void printStr(){
System.out.println("Hello:->" + str1);
}
}

\n
    \n
  1. 在resources下创建beans.xml配置文件
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="helloworld" class="com.ajream.pojo.Hello">
<!-- 通过name来识别出调用了哪个函数, value为传入参数-->
<property name="str2" value="spring" /> <!--这行代码表示调用函数 setStr2, 传入参数为字符串"spring" -->

</bean>
</beans>
\n
\n

该文件内容模板可以从spring官网中找到

\n

\"image-20210729131316578\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions go here -->

</beans>
\n
\n
    \n
  1. 在test/java下创建MyTest类来进行测试输出:
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import com.ajream.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //加载xml配置文件
Hello h = (Hello) context.getBean("helloworld");
h.printStr();
}
}

\n

输出:

\n

\"image-20210729131704285\"

\n

三者之间的关系

\"image-20210729134624753\"

\n

执行流程:

\n
    \n
  1. 获取 beans.xml 配置文件信息
  2. \n
  3. 根据beans.xml中的id获取bean,创建对象h,并调用了setStr2方法
  4. \n
  5. 调用函数printStr
  6. \n
\n"},{"title":"Spring(5)-ioc创建对象方式","abbrlink":"d8642fb9","date":"2021-07-26T06:58:44.000Z","description":"spring如何使用配置文件来创建对象、注入属性","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n# ioc创建对象方式\n\n\n\n## 构造函数注入\n\nUser.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public User(String name){ //构造函数\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n\n```\n\n\n\n1. 下标指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n2. 变量名指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n3. 类型指定参数(不建议,有可能多个参数的类型相同)\n\n ```xml\n \n \n \n ```\n\n4. 构造函数没有参数的话\n\n ```xml\n \n \n ```\n\n\n\n## set注入\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public void setName(String name){ // set注入\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n```\n\n\n\n```xml\n\n \n\n```\n\n","source":"_posts/Spring/5-ioc创建对象方式.md","raw":"---\ntitle: Spring(5)-ioc创建对象方式\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: d8642fb9\ndate: 2021-07-26 14:58:44\ndescription: spring如何使用配置文件来创建对象、注入属性\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n# ioc创建对象方式\n\n\n\n## 构造函数注入\n\nUser.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public User(String name){ //构造函数\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n\n```\n\n\n\n1. 下标指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n2. 变量名指定参数\n\n ```xml\n \n \n \n ```\n\n \n\n3. 类型指定参数(不建议,有可能多个参数的类型相同)\n\n ```xml\n \n \n \n ```\n\n4. 构造函数没有参数的话\n\n ```xml\n \n \n ```\n\n\n\n## set注入\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String name;\n\n public void setName(String name){ // set注入\n this.name = name;\n }\n\n public String getName(){\n return name;\n }\n\n public void show(){\n System.out.println(\"name: \" + name);\n }\n}\n```\n\n\n\n```xml\n\n \n\n```\n\n","slug":"Spring/5-ioc创建对象方式","published":1,"updated":"2021-09-10T03:18:20.605Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pe002takvedtbz9vw9","content":"

ioc创建对象方式

构造函数注入

User.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.pojo;

public class User {
private String name;

public User(String name){ //构造函数
this.name = name;
}

public String getName(){
return name;
}

public void show(){
System.out.println("name: " + name);
}
}

\n
    \n
  1. 下标指定参数

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg index="0" value="张三" /> <!-- 0表示第一个参数-->
    </bean>
    \n
  2. \n
\n
    \n
  1. 变量名指定参数

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg name="name" value="李四"/>
    </bean>
    \n
  2. \n
\n
    \n
  1. 类型指定参数(不建议,有可能多个参数的类型相同)

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg type="String" value="王五" />
    </bean>
    \n
  2. \n
  3. 构造函数没有参数的话

    \n
    1
    2
    <bean id="user" class="com.ajream.pojo.User">
    </bean>
    \n
  4. \n
\n

set注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.pojo;

public class User {
private String name;

public void setName(String name){ // set注入
this.name = name;
}

public String getName(){
return name;
}

public void show(){
System.out.println("name: " + name);
}
}
\n
1
2
3
<bean id="user" class="com.ajream.pojo.User">
<property name="name" value="李四"/>
</bean>
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

ioc创建对象方式

构造函数注入

User.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.pojo;

public class User {
private String name;

public User(String name){ //构造函数
this.name = name;
}

public String getName(){
return name;
}

public void show(){
System.out.println("name: " + name);
}
}

\n
    \n
  1. 下标指定参数

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg index="0" value="张三" /> <!-- 0表示第一个参数-->
    </bean>
    \n
  2. \n
\n
    \n
  1. 变量名指定参数

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg name="name" value="李四"/>
    </bean>
    \n
  2. \n
\n
    \n
  1. 类型指定参数(不建议,有可能多个参数的类型相同)

    \n
    1
    2
    3
    <bean id="user" class="com.ajream.pojo.User">
    <constructor-arg type="String" value="王五" />
    </bean>
    \n
  2. \n
  3. 构造函数没有参数的话

    \n
    1
    2
    <bean id="user" class="com.ajream.pojo.User">
    </bean>
    \n
  4. \n
\n

set注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.pojo;

public class User {
private String name;

public void setName(String name){ // set注入
this.name = name;
}

public String getName(){
return name;
}

public void show(){
System.out.println("name: " + name);
}
}
\n
1
2
3
<bean id="user" class="com.ajream.pojo.User">
<property name="name" value="李四"/>
</bean>
\n"},{"title":"Spring(3)-ioc控制反转","abbrlink":"d959138b","date":"2021-07-25T05:25:37.000Z","description":"spring控制反转(IOC)思想举例理解","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n# IOC控制反转思想举例\n\n\n\n## 创建工程\n\n利用Maven创建一个空白工程spring1,其目录结构如下:\n\n![image-20210728212756154](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-27-57_image-20210728212756154.png)\n\n\n\n然后创建以下几个接口和类:\n\n1. Dao层\n2. Service业务层\n3. 用户层(test表示用户测试)\n\n\n![image-20210728213117047](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-31-18_image-20210728213117047.png)\n\n\n\n\n\n## 代码实现\n\n目标:用户要在test1中创建一个对象,调用Dao层的某个方法 `getUser()`来实现某种功能\n\n\n\n\n\n### Dao层\n\nUserDao接口:\n\n```java\npackage com.ajream.Dao;\n\npublic interface UserDao {\n void getUser();\n}\n```\n\nUserDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserDaoImpl implements UserDao{\n @Override\n public void getUser() {\n System.out.println(\"默认获取用户数据。。。\");\n }\n}\n```\n\nUserMysqlDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserMysqlDaoImpl implements UserDao{\n\n @Override\n public void getUser() {\n System.out.println(\"MySQL获取用户数据。。。\");\n }\n}\n\n```\n\n\n\n\n\n\n\n### 没有使用控制反转时的业务层\n\nUserService接口:\n\n```java\npackage com.ajream.Service;\n\npublic interface UserService {\n void getUser();\n}\n```\n\nUserServiceImpl:\n\n![image-20210728215047346](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-50-48_image-20210728215047346.png)\n\n即:\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\nimport com.ajream.Dao.UserDaoImpl;\n\npublic class UserServiceImpl implements UserService {\n \n private UserDao userDao = new UserDaoImpl(); //注意此处\n\n public UserServiceImpl() {\n }\n\n public void getUser() {\n this.userDao.getUser();\n }\n}\n```\n\n\n\n> 注意没有使用IOC时第8行代码,这样写的缺点:\n>\n> 这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:\n>\n> ```java\n> private UserDao userDao = new UserMysqlDaoImpl();\n> ```\n>\n> 很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现\n\n\n\n\n\n### 使用控制反转后的业务层\n\nUserService接口不变\n\nUserServiceImpl使用setUser来创建对象:\n\n![image-20210728220123436](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-01-24_image-20210728220123436.png)\n\n\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\n\n\npublic class UserServiceImpl implements UserService{\n private UserDao userDao;\n @Override\n public void getUser() {\n userDao.getUser();\n }\n public void setUser(UserDao userDao){\n this.userDao = userDao;\n }\n}\n```\n\n> 用户可以通过调用`setUser()`方法来创建不同对象(只需要传入不同参数即可)\n\n\n\n对比:\n\n![image-20210728221513985](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-15-15_image-20210728221513985.png)\n\n---\n\n\n\ntest1(这里是用户调用业务层实现业务的部分)\n\n- 没有使用IOC时\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n\n userService.getUser();\n }\n}\n\n```\n\n\n\n- 使用了IOC的思想后\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n \n// 通过修改setUser参数来反向创建不同对象\n ((UserServiceImpl) userService).setUser(new UserDaoImpl()); \n userService.getUser();\n \n ((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl());\n userService.getUser();\n }\n}\n\n```\n\n\n\n对比:\n\n![image-20210728221756345](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-17-57_image-20210728221756345.png)\n\n\n\n\n\n## 一张图表示三者之间关系\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n用户希望通过调用业务层来实现各个具体的业务如 `Mysql`、`Oracle`,...\n\n\n\n","source":"_posts/Spring/3-ioc控制反转.md","raw":"---\ntitle: Spring(3)-ioc控制反转\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: d959138b\ndate: 2021-07-25 13:25:37\ndescription: spring控制反转(IOC)思想举例理解\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n# IOC控制反转思想举例\n\n\n\n## 创建工程\n\n利用Maven创建一个空白工程spring1,其目录结构如下:\n\n![image-20210728212756154](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-27-57_image-20210728212756154.png)\n\n\n\n然后创建以下几个接口和类:\n\n1. Dao层\n2. Service业务层\n3. 用户层(test表示用户测试)\n\n\n![image-20210728213117047](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-31-18_image-20210728213117047.png)\n\n\n\n\n\n## 代码实现\n\n目标:用户要在test1中创建一个对象,调用Dao层的某个方法 `getUser()`来实现某种功能\n\n\n\n\n\n### Dao层\n\nUserDao接口:\n\n```java\npackage com.ajream.Dao;\n\npublic interface UserDao {\n void getUser();\n}\n```\n\nUserDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserDaoImpl implements UserDao{\n @Override\n public void getUser() {\n System.out.println(\"默认获取用户数据。。。\");\n }\n}\n```\n\nUserMysqlDaoImpl:\n\n```java\npackage com.ajream.Dao;\n\npublic class UserMysqlDaoImpl implements UserDao{\n\n @Override\n public void getUser() {\n System.out.println(\"MySQL获取用户数据。。。\");\n }\n}\n\n```\n\n\n\n\n\n\n\n### 没有使用控制反转时的业务层\n\nUserService接口:\n\n```java\npackage com.ajream.Service;\n\npublic interface UserService {\n void getUser();\n}\n```\n\nUserServiceImpl:\n\n![image-20210728215047346](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-50-48_image-20210728215047346.png)\n\n即:\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\nimport com.ajream.Dao.UserDaoImpl;\n\npublic class UserServiceImpl implements UserService {\n \n private UserDao userDao = new UserDaoImpl(); //注意此处\n\n public UserServiceImpl() {\n }\n\n public void getUser() {\n this.userDao.getUser();\n }\n}\n```\n\n\n\n> 注意没有使用IOC时第8行代码,这样写的缺点:\n>\n> 这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:\n>\n> ```java\n> private UserDao userDao = new UserMysqlDaoImpl();\n> ```\n>\n> 很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现\n\n\n\n\n\n### 使用控制反转后的业务层\n\nUserService接口不变\n\nUserServiceImpl使用setUser来创建对象:\n\n![image-20210728220123436](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-01-24_image-20210728220123436.png)\n\n\n\n```java\npackage com.ajream.Service;\n\nimport com.ajream.Dao.UserDao;\n\n\npublic class UserServiceImpl implements UserService{\n private UserDao userDao;\n @Override\n public void getUser() {\n userDao.getUser();\n }\n public void setUser(UserDao userDao){\n this.userDao = userDao;\n }\n}\n```\n\n> 用户可以通过调用`setUser()`方法来创建不同对象(只需要传入不同参数即可)\n\n\n\n对比:\n\n![image-20210728221513985](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-15-15_image-20210728221513985.png)\n\n---\n\n\n\ntest1(这里是用户调用业务层实现业务的部分)\n\n- 没有使用IOC时\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n\n userService.getUser();\n }\n}\n\n```\n\n\n\n- 使用了IOC的思想后\n\n```java\npackage com.ajream;\n\nimport com.ajream.Dao.UserDaoImpl;\nimport com.ajream.Dao.UserMysqlDaoImpl;\nimport com.ajream.Service.UserService;\nimport com.ajream.Service.UserServiceImpl;\n\npublic class test1 {\n public static void main(String[] args) {\n \n UserService userService = new UserServiceImpl();\n \n// 通过修改setUser参数来反向创建不同对象\n ((UserServiceImpl) userService).setUser(new UserDaoImpl()); \n userService.getUser();\n \n ((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl());\n userService.getUser();\n }\n}\n\n```\n\n\n\n对比:\n\n![image-20210728221756345](https://gitee.com/ajream/images/raw/master/img/2021-07-2822-17-57_image-20210728221756345.png)\n\n\n\n\n\n## 一张图表示三者之间关系\n\n![ioc](https://gitee.com/ajream/images/raw/master/img/2021-07-2821-17-37_ioc.svg)\n\n\n\n用户希望通过调用业务层来实现各个具体的业务如 `Mysql`、`Oracle`,...\n\n\n\n","slug":"Spring/3-ioc控制反转","published":1,"updated":"2021-09-10T03:17:53.109Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pf002xakve102r3c73","content":"

IOC控制反转思想举例

创建工程

利用Maven创建一个空白工程spring1,其目录结构如下:

\n

\"image-20210728212756154\"

\n

然后创建以下几个接口和类:

\n
    \n
  1. Dao层
  2. \n
  3. Service业务层
  4. \n
  5. 用户层(test表示用户测试)
  6. \n
\n

\"image-20210728213117047\"

\n

代码实现

目标:用户要在test1中创建一个对象,调用Dao层的某个方法 getUser()来实现某种功能

\n

Dao层

UserDao接口:

\n
1
2
3
4
5
package com.ajream.Dao;

public interface UserDao {
void getUser();
}
\n

UserDaoImpl:

\n
1
2
3
4
5
6
7
8
package com.ajream.Dao;

public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("默认获取用户数据。。。");
}
}
\n

UserMysqlDaoImpl:

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.Dao;

public class UserMysqlDaoImpl implements UserDao{

@Override
public void getUser() {
System.out.println("MySQL获取用户数据。。。");
}
}

\n

没有使用控制反转时的业务层

UserService接口:

\n
1
2
3
4
5
package com.ajream.Service;

public interface UserService {
void getUser();
}
\n

UserServiceImpl:

\n

\"image-20210728215047346\"

\n

即:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.Service;

import com.ajream.Dao.UserDao;
import com.ajream.Dao.UserDaoImpl;

public class UserServiceImpl implements UserService {

private UserDao userDao = new UserDaoImpl(); //注意此处

public UserServiceImpl() {
}

public void getUser() {
this.userDao.getUser();
}
}
\n
\n

注意没有使用IOC时第8行代码,这样写的缺点:

\n

这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:

\n
1
private UserDao userDao = new UserMysqlDaoImpl();
\n

很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现

\n
\n

使用控制反转后的业务层

UserService接口不变

\n

UserServiceImpl使用setUser来创建对象:

\n

\"image-20210728220123436\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.Service;

import com.ajream.Dao.UserDao;


public class UserServiceImpl implements UserService{
private UserDao userDao;
@Override
public void getUser() {
userDao.getUser();
}
public void setUser(UserDao userDao){
this.userDao = userDao;
}
}
\n
\n

用户可以通过调用setUser()方法来创建不同对象(只需要传入不同参数即可)

\n
\n

对比:

\n

\"image-20210728221513985\"

\n
\n

test1(这里是用户调用业务层实现业务的部分)

\n
    \n
  • 没有使用IOC时
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream;

import com.ajream.Dao.UserDaoImpl;
import com.ajream.Dao.UserMysqlDaoImpl;
import com.ajream.Service.UserService;
import com.ajream.Service.UserServiceImpl;

public class test1 {
public static void main(String[] args) {

UserService userService = new UserServiceImpl();

userService.getUser();
}
}

\n
    \n
  • 使用了IOC的思想后
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ajream;

import com.ajream.Dao.UserDaoImpl;
import com.ajream.Dao.UserMysqlDaoImpl;
import com.ajream.Service.UserService;
import com.ajream.Service.UserServiceImpl;

public class test1 {
public static void main(String[] args) {

UserService userService = new UserServiceImpl();

// 通过修改setUser参数来反向创建不同对象
((UserServiceImpl) userService).setUser(new UserDaoImpl());
userService.getUser();

((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl());
userService.getUser();
}
}

\n

对比:

\n

\"image-20210728221756345\"

\n

一张图表示三者之间关系

\"ioc\"

\n

用户希望通过调用业务层来实现各个具体的业务如 MysqlOracle,…

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

IOC控制反转思想举例

创建工程

利用Maven创建一个空白工程spring1,其目录结构如下:

\n

\"image-20210728212756154\"

\n

然后创建以下几个接口和类:

\n
    \n
  1. Dao层
  2. \n
  3. Service业务层
  4. \n
  5. 用户层(test表示用户测试)
  6. \n
\n

\"image-20210728213117047\"

\n

代码实现

目标:用户要在test1中创建一个对象,调用Dao层的某个方法 getUser()来实现某种功能

\n

Dao层

UserDao接口:

\n
1
2
3
4
5
package com.ajream.Dao;

public interface UserDao {
void getUser();
}
\n

UserDaoImpl:

\n
1
2
3
4
5
6
7
8
package com.ajream.Dao;

public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("默认获取用户数据。。。");
}
}
\n

UserMysqlDaoImpl:

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.Dao;

public class UserMysqlDaoImpl implements UserDao{

@Override
public void getUser() {
System.out.println("MySQL获取用户数据。。。");
}
}

\n

没有使用控制反转时的业务层

UserService接口:

\n
1
2
3
4
5
package com.ajream.Service;

public interface UserService {
void getUser();
}
\n

UserServiceImpl:

\n

\"image-20210728215047346\"

\n

即:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.Service;

import com.ajream.Dao.UserDao;
import com.ajream.Dao.UserDaoImpl;

public class UserServiceImpl implements UserService {

private UserDao userDao = new UserDaoImpl(); //注意此处

public UserServiceImpl() {
}

public void getUser() {
this.userDao.getUser();
}
}
\n
\n

注意没有使用IOC时第8行代码,这样写的缺点:

\n

这样指定创建了一个对象UserDaoImpl,假如我要创建另一个对象UserMysqlDaoImpl,用户就需要修改业务层这一行代码,例如改为:

\n
1
private UserDao userDao = new UserMysqlDaoImpl();
\n

很明显,这样有些不方便,作为用户,应该不需要关心业务的具体实现

\n
\n

使用控制反转后的业务层

UserService接口不变

\n

UserServiceImpl使用setUser来创建对象:

\n

\"image-20210728220123436\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.Service;

import com.ajream.Dao.UserDao;


public class UserServiceImpl implements UserService{
private UserDao userDao;
@Override
public void getUser() {
userDao.getUser();
}
public void setUser(UserDao userDao){
this.userDao = userDao;
}
}
\n
\n

用户可以通过调用setUser()方法来创建不同对象(只需要传入不同参数即可)

\n
\n

对比:

\n

\"image-20210728221513985\"

\n
\n

test1(这里是用户调用业务层实现业务的部分)

\n
    \n
  • 没有使用IOC时
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream;

import com.ajream.Dao.UserDaoImpl;
import com.ajream.Dao.UserMysqlDaoImpl;
import com.ajream.Service.UserService;
import com.ajream.Service.UserServiceImpl;

public class test1 {
public static void main(String[] args) {

UserService userService = new UserServiceImpl();

userService.getUser();
}
}

\n
    \n
  • 使用了IOC的思想后
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ajream;

import com.ajream.Dao.UserDaoImpl;
import com.ajream.Dao.UserMysqlDaoImpl;
import com.ajream.Service.UserService;
import com.ajream.Service.UserServiceImpl;

public class test1 {
public static void main(String[] args) {

UserService userService = new UserServiceImpl();

// 通过修改setUser参数来反向创建不同对象
((UserServiceImpl) userService).setUser(new UserDaoImpl());
userService.getUser();

((UserServiceImpl) userService).setUser(new UserMysqlDaoImpl());
userService.getUser();
}
}

\n

对比:

\n

\"image-20210728221756345\"

\n

一张图表示三者之间关系

\"ioc\"

\n

用户希望通过调用业务层来实现各个具体的业务如 MysqlOracle,…

\n"},{"title":"Spring(7)-spring容器注入","abbrlink":"7f2244ba","date":"2021-07-27T06:39:25.000Z","description":"spring容器类型如何注入","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n\n# spring容器注入\n\n [跳转到项目地址](https://codechina.csdn.net/m0_46079750/spring4/-/tree/master)\n\n\n\n如果是`String` 这种简单类型的数据,spring注入如下:\n\n\n\nUser.java\n\n```java\npublic class User{\n private String name;\n \n public void setName(String name){\n this.name = name;\n }\n \n //...\n}\n```\n\nbeans.xml\n\n```xml\n\n\n\t\n\n\n```\n\n\n\n但是,如果要注入的不是如`String` 这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:\n\n[官方文档说明](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/core.html#beans-inner-beans)\n\n```xml\n\n \n \n \n 《abc》\n 《defg》\n 《hijk》\n \n \n \n \n \n \n administrator@example.org\n support@example.org\n development@example.org\n \n \n \n \n \n \n a list element followed by a reference\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n just some string\n \n \n \n\n```\n\n\n\n例如要往下面这些属性注入值\n\n![image-20210730172726839](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-27-28_image-20210730172726839.png)\n\nbeans.xml:\n\n```xml\n\n\n\n \n \n \n\n \n \n\n \n \n\n\n \n\n\n \n \n 《计算机技术》\n 《操作系统》\n 《数据结构》\n \n \n\n\n \n \n 王者荣耀\n 少年三国志\n \n \n\n\n \n \n 打游戏\n 打羽毛球\n \n \n\n\n \n \n \n \n \n \n \n\n\n \n \n administrator@example.org\n 13389453895\n 456382987\n \n \n\n \n\n\n```\n\n\n\n\n\n输出预览:\n\n![image-20210730173016148](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-30-17_image-20210730173016148.png)\n\n","source":"_posts/Spring/7-spring容器注入.md","raw":"---\ntitle: Spring(7)-spring容器注入\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 7f2244ba\ndate: 2021-07-27 14:39:25\ndescription: spring容器类型如何注入\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n\n# spring容器注入\n\n [跳转到项目地址](https://codechina.csdn.net/m0_46079750/spring4/-/tree/master)\n\n\n\n如果是`String` 这种简单类型的数据,spring注入如下:\n\n\n\nUser.java\n\n```java\npublic class User{\n private String name;\n \n public void setName(String name){\n this.name = name;\n }\n \n //...\n}\n```\n\nbeans.xml\n\n```xml\n\n\n\t\n\n\n```\n\n\n\n但是,如果要注入的不是如`String` 这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:\n\n[官方文档说明](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/core.html#beans-inner-beans)\n\n```xml\n\n \n \n \n 《abc》\n 《defg》\n 《hijk》\n \n \n \n \n \n \n administrator@example.org\n support@example.org\n development@example.org\n \n \n \n \n \n \n a list element followed by a reference\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n just some string\n \n \n \n\n```\n\n\n\n例如要往下面这些属性注入值\n\n![image-20210730172726839](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-27-28_image-20210730172726839.png)\n\nbeans.xml:\n\n```xml\n\n\n\n \n \n \n\n \n \n\n \n \n\n\n \n\n\n \n \n 《计算机技术》\n 《操作系统》\n 《数据结构》\n \n \n\n\n \n \n 王者荣耀\n 少年三国志\n \n \n\n\n \n \n 打游戏\n 打羽毛球\n \n \n\n\n \n \n \n \n \n \n \n\n\n \n \n administrator@example.org\n 13389453895\n 456382987\n \n \n\n \n\n\n```\n\n\n\n\n\n输出预览:\n\n![image-20210730173016148](https://gitee.com/ajream/images/raw/master/img/2021-07-3017-30-17_image-20210730173016148.png)\n\n","slug":"Spring/7-spring容器注入","published":1,"updated":"2021-09-10T03:18:32.070Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pg002zakve9wvvbwrm","content":"

spring容器注入

跳转到项目地址

\n

如果是String 这种简单类型的数据,spring注入如下:

\n

User.java

\n
1
2
3
4
5
6
7
8
9
public class User{
private String name;

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

//...
}
\n

beans.xml

\n
1
2
3
4
5
<!--前面的省略-->
<bean id="user" class="com.xxx.User">
\t<property name="name" value="张三" />
</bean>

\n

但是,如果要注入的不是如String 这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:

\n

官方文档说明

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- array注入 -->
<property name="books">
<array>
<value>《abc》</value>
<value>《defg》</value>
<value>《hijk》</value>
</array>
</property>

<!-- prop注入 -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>

<!-- list注入 -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>

<!-- map -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>

<!-- set注入 -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
\n

例如要往下面这些属性注入值

\n

\"image-20210730172726839\"

\n

beans.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="address" class="com.ajream.pojo.Address" >
<property name="addr" value="西安"/>
</bean>

<bean id="student" class="com.ajream.pojo.Student">

<!-- 普通值注入-->
<property name="name" value="王二"/>
<property name="age" value="30"/>

<!-- bean注入-->
<property name="address" ref="address" />

<!-- 数组array注入-->
<property name="books">
<array>
<value>《计算机技术》</value>
<value>《操作系统》</value>
<value>《数据结构》</value>
</array>
</property>

<!-- set注入-->
<property name="games">
<set>
<value>王者荣耀</value>
<value>少年三国志</value>
</set>
</property>

<!-- list注入-->
<property name="hobbies">
<list>
<value>打游戏</value>
<value>打羽毛球</value>
</list>
</property>

<!-- map注入-->
<property name="grade">
<map>
<entry key="高等数学" value="85"/>
<entry key="大学英语" value="90"/>
<entry key="java基础" value="98"/>
</map>
</property>

<!-- prop注入,类似map-->
<property name="info">
<props>
<prop key="email">administrator@example.org</prop>
<prop key="phone">13389453895</prop>
<prop key="QQ-number">456382987</prop>
</props>
</property>

</bean>

</beans>
\n

输出预览:

\n

\"image-20210730173016148\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

spring容器注入

跳转到项目地址

\n

如果是String 这种简单类型的数据,spring注入如下:

\n

User.java

\n
1
2
3
4
5
6
7
8
9
public class User{
private String name;

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

//...
}
\n

beans.xml

\n
1
2
3
4
5
<!--前面的省略-->
<bean id="user" class="com.xxx.User">
\t<property name="name" value="张三" />
</bean>

\n

但是,如果要注入的不是如String 这种简单类型数据,而是array、list、map、prop。。。这种容器类型数据,应该如何写bean,下面是模板:

\n

官方文档说明

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- array注入 -->
<property name="books">
<array>
<value>《abc》</value>
<value>《defg》</value>
<value>《hijk》</value>
</array>
</property>

<!-- prop注入 -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>

<!-- list注入 -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>

<!-- map -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>

<!-- set注入 -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
\n

例如要往下面这些属性注入值

\n

\"image-20210730172726839\"

\n

beans.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="address" class="com.ajream.pojo.Address" >
<property name="addr" value="西安"/>
</bean>

<bean id="student" class="com.ajream.pojo.Student">

<!-- 普通值注入-->
<property name="name" value="王二"/>
<property name="age" value="30"/>

<!-- bean注入-->
<property name="address" ref="address" />

<!-- 数组array注入-->
<property name="books">
<array>
<value>《计算机技术》</value>
<value>《操作系统》</value>
<value>《数据结构》</value>
</array>
</property>

<!-- set注入-->
<property name="games">
<set>
<value>王者荣耀</value>
<value>少年三国志</value>
</set>
</property>

<!-- list注入-->
<property name="hobbies">
<list>
<value>打游戏</value>
<value>打羽毛球</value>
</list>
</property>

<!-- map注入-->
<property name="grade">
<map>
<entry key="高等数学" value="85"/>
<entry key="大学英语" value="90"/>
<entry key="java基础" value="98"/>
</map>
</property>

<!-- prop注入,类似map-->
<property name="info">
<props>
<prop key="email">administrator@example.org</prop>
<prop key="phone">13389453895</prop>
<prop key="QQ-number">456382987</prop>
</props>
</property>

</bean>

</beans>
\n

输出预览:

\n

\"image-20210730173016148\"

\n"},{"title":"Spring(6)-spring配置说明","abbrlink":"3fb3b16d","date":"2021-07-26T05:26:19.000Z","description":"spring一些基本配置","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n\n\n# spring配置\n\n\n\n## 别名alias\n\nbean可以拥有别名\n\n```xml\n\n```\n\n\n\n例如:\n\n```xml\n\n \n \n\n\n \n```\n\n\n\n使用别名后就可以通过别名来获取bean\n\n```java\n//User user = context.getBean(\"user\", User.class);\nUser user = context.getBean(\"user2\", User.class);\n```\n\n\n\n## bean的配置\n\n```xml\n\n\n \n\n```\n\n\n\n## import\n\n如果有多个 `beans`配置文件,可以用 `import`将多个文件导入到一个文件中\n\n例如几个配置文件如下:\n\n```\nresources\n\t|-applicationcontext.xml\n\t|-beans1.xml\n\t|-beans2.xml\n```\n\n在 `applicationcontext.xml` 中可以使用 `import` 标签导入 另外2个配置文件:\n\n```xml\n\n\n```\n\n> 适用于团队合作时使用\n\n\n\n","source":"_posts/Spring/6-spring配置说明.md","raw":"---\ntitle: Spring(6)-spring配置说明\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 3fb3b16d\ndate: 2021-07-26 13:26:19\ndescription: spring一些基本配置\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n\n\n# spring配置\n\n\n\n## 别名alias\n\nbean可以拥有别名\n\n```xml\n\n```\n\n\n\n例如:\n\n```xml\n\n \n \n\n\n \n```\n\n\n\n使用别名后就可以通过别名来获取bean\n\n```java\n//User user = context.getBean(\"user\", User.class);\nUser user = context.getBean(\"user2\", User.class);\n```\n\n\n\n## bean的配置\n\n```xml\n\n\n \n\n```\n\n\n\n## import\n\n如果有多个 `beans`配置文件,可以用 `import`将多个文件导入到一个文件中\n\n例如几个配置文件如下:\n\n```\nresources\n\t|-applicationcontext.xml\n\t|-beans1.xml\n\t|-beans2.xml\n```\n\n在 `applicationcontext.xml` 中可以使用 `import` 标签导入 另外2个配置文件:\n\n```xml\n\n\n```\n\n> 适用于团队合作时使用\n\n\n\n","slug":"Spring/6-spring配置说明","published":1,"updated":"2021-09-10T03:18:24.710Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pi0032akve2d4z141s","content":"

spring配置

别名alias

bean可以拥有别名

\n
1
<alias name="fromName" alias="toName"/>
\n

例如:

\n
1
2
3
4
5
6
<bean id="user" class="com.ajream.pojo.User">
<property name="name" value="章三" />
<property name="age" value="20" />
</bean>

<alias name="user" alias="user2"/>
\n

使用别名后就可以通过别名来获取bean

\n
1
2
//User user = context.getBean("user", User.class);
User user = context.getBean("user2", User.class);
\n

bean的配置

1
2
3
4
5
6
7
8
<!--
id: bean的唯一标识符,相当于创建对象`User user = new User()`的对象名user
class:指向的类
name:也是别名,并且可以取多个,中间可以用逗号、分号、空格隔开
-->
<bean id="user" class="com.ajream.pojo.User" name="n1,n2;n3 n4">
<property name="name" value="章三" />
</bean>
\n

import

如果有多个 beans配置文件,可以用 import将多个文件导入到一个文件中

\n

例如几个配置文件如下:

\n
1
2
3
4
resources
\t|-applicationcontext.xml
\t|-beans1.xml
\t|-beans2.xml
\n

applicationcontext.xml 中可以使用 import 标签导入 另外2个配置文件:

\n
1
2
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
\n
\n

适用于团队合作时使用

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

spring配置

别名alias

bean可以拥有别名

\n
1
<alias name="fromName" alias="toName"/>
\n

例如:

\n
1
2
3
4
5
6
<bean id="user" class="com.ajream.pojo.User">
<property name="name" value="章三" />
<property name="age" value="20" />
</bean>

<alias name="user" alias="user2"/>
\n

使用别名后就可以通过别名来获取bean

\n
1
2
//User user = context.getBean("user", User.class);
User user = context.getBean("user2", User.class);
\n

bean的配置

1
2
3
4
5
6
7
8
<!--
id: bean的唯一标识符,相当于创建对象`User user = new User()`的对象名user
class:指向的类
name:也是别名,并且可以取多个,中间可以用逗号、分号、空格隔开
-->
<bean id="user" class="com.ajream.pojo.User" name="n1,n2;n3 n4">
<property name="name" value="章三" />
</bean>
\n

import

如果有多个 beans配置文件,可以用 import将多个文件导入到一个文件中

\n

例如几个配置文件如下:

\n
1
2
3
4
resources
\t|-applicationcontext.xml
\t|-beans1.xml
\t|-beans2.xml
\n

applicationcontext.xml 中可以使用 import 标签导入 另外2个配置文件:

\n
1
2
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
\n
\n

适用于团队合作时使用

\n
\n"},{"title":"Spring(9)-bean自动装配","abbrlink":"f68104ec","date":"2021-07-28T09:26:49.000Z","description":"spring自动装配(自动根据一些配置说明来注入属性)的使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n# bean自动装配\n\n\n\n[![spring5](https://codechina.csdn.net/m0_46079750/spring5/-/card.svg)](https://codechina.csdn.net/m0_46079750/spring5)\n\n## 代码\n\n用一个项目来说明,如下:\n\n![image-20210731164147058](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-41-53_image-20210731164147058.png)\n\n其中各个文件代码如下:\n\n\n\nCat:\n\n```java\npackage com.ajream.pojo;\n\npublic class Cat {\n public void shout(){\n System.out.println(\"miao~\");\n }\n}\n```\n\n\n\nDog:\n\n```java\npackage com.ajream.pojo;\n\npublic class Dog {\n public void shout(){\n System.out.println(\"wang~\");\n }\n}\n\n```\n\n\n\nPerson:\n\n```java\npackage com.ajream.pojo;\n\npublic class Person {\n private String name;\n private Cat cat;\n private Dog dog;\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public Cat getCat() {\n return cat;\n }\n\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n public Dog getDog() {\n return dog;\n }\n\n public void setDog(Dog dog) {\n this.dog = dog;\n }\n\n @Override\n public String toString() {\n return \"Person{\" +\n \"name='\" + name + '\\'' +\n \", cat=\" + cat +\n \", dog=\" + dog +\n '}';\n }\n\n}\n\n```\n\nbeans.xml\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n```\n\n\n\nMyTest:\n\n```java\nimport com.ajream.pojo.Person;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\n\npublic class MyTest {\n\n public static void main(String[] args) {\n \n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\");\n Person p = context.getBean(\"person\", Person.class);\n\n System.out.println(p.getName());\n p.getCat().shout();\n p.getDog().shout();\n\n }\n\n}\n\n```\n\n\n\n## 分析\n\n主要看 `Person`类和 `beans.xml`文件,在没有使用自动装配时\n\n![image-20210731165352656](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-53-57_image-20210731165352656.png)\n\n\n\n1. 使用 `byName` 自动装配\n\n![image-20210731170102634](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-01-06_image-20210731170102634.png)\n\n\n\n使用自动装配就添加了 `autowire = \"byName\"` 这一配置,要求如下:\n\n![image-20210731170646544](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-06-49_image-20210731170646544.png)\n\nbyName就相当于`name`和`ref`相同的情形(这也是一般情形),而由于`name`又与 `setName` 关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:\n\n![image-20210731171212541](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-12-15_image-20210731171212541.png)\n\n\n\n\n\n2. 使用 `byType` 自动装配\n\n\n\n对id名无要求,但要求bean类型唯一\n\n![image-20210731171743101](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-17-46_image-20210731171743101.png)\n\n比如下面这种情况就会出错:\n\n![image-20210731172014553](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-20-19_image-20210731172014553.png)\n\n\n\n> 总结:\n>\n> - 使用byName方式的自动装配,bean的id要与set方法保持一致\n> - 使用byType方式的自动装配,bean的类型要唯一,否则就会出错\n\n","source":"_posts/Spring/9-bean自动装配.md","raw":"---\ntitle: Spring(9)-bean自动装配\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: f68104ec\ndate: 2021-07-28 17:26:49\ndescription: spring自动装配(自动根据一些配置说明来注入属性)的使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n# bean自动装配\n\n\n\n[![spring5](https://codechina.csdn.net/m0_46079750/spring5/-/card.svg)](https://codechina.csdn.net/m0_46079750/spring5)\n\n## 代码\n\n用一个项目来说明,如下:\n\n![image-20210731164147058](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-41-53_image-20210731164147058.png)\n\n其中各个文件代码如下:\n\n\n\nCat:\n\n```java\npackage com.ajream.pojo;\n\npublic class Cat {\n public void shout(){\n System.out.println(\"miao~\");\n }\n}\n```\n\n\n\nDog:\n\n```java\npackage com.ajream.pojo;\n\npublic class Dog {\n public void shout(){\n System.out.println(\"wang~\");\n }\n}\n\n```\n\n\n\nPerson:\n\n```java\npackage com.ajream.pojo;\n\npublic class Person {\n private String name;\n private Cat cat;\n private Dog dog;\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public Cat getCat() {\n return cat;\n }\n\n public void setCat(Cat cat) {\n this.cat = cat;\n }\n\n public Dog getDog() {\n return dog;\n }\n\n public void setDog(Dog dog) {\n this.dog = dog;\n }\n\n @Override\n public String toString() {\n return \"Person{\" +\n \"name='\" + name + '\\'' +\n \", cat=\" + cat +\n \", dog=\" + dog +\n '}';\n }\n\n}\n\n```\n\nbeans.xml\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n```\n\n\n\nMyTest:\n\n```java\nimport com.ajream.pojo.Person;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\n\npublic class MyTest {\n\n public static void main(String[] args) {\n \n ApplicationContext context = new ClassPathXmlApplicationContext(\"beans.xml\");\n Person p = context.getBean(\"person\", Person.class);\n\n System.out.println(p.getName());\n p.getCat().shout();\n p.getDog().shout();\n\n }\n\n}\n\n```\n\n\n\n## 分析\n\n主要看 `Person`类和 `beans.xml`文件,在没有使用自动装配时\n\n![image-20210731165352656](https://gitee.com/ajream/images/raw/master/img/2021-07-3116-53-57_image-20210731165352656.png)\n\n\n\n1. 使用 `byName` 自动装配\n\n![image-20210731170102634](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-01-06_image-20210731170102634.png)\n\n\n\n使用自动装配就添加了 `autowire = \"byName\"` 这一配置,要求如下:\n\n![image-20210731170646544](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-06-49_image-20210731170646544.png)\n\nbyName就相当于`name`和`ref`相同的情形(这也是一般情形),而由于`name`又与 `setName` 关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:\n\n![image-20210731171212541](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-12-15_image-20210731171212541.png)\n\n\n\n\n\n2. 使用 `byType` 自动装配\n\n\n\n对id名无要求,但要求bean类型唯一\n\n![image-20210731171743101](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-17-46_image-20210731171743101.png)\n\n比如下面这种情况就会出错:\n\n![image-20210731172014553](https://gitee.com/ajream/images/raw/master/img/2021-07-3117-20-19_image-20210731172014553.png)\n\n\n\n> 总结:\n>\n> - 使用byName方式的自动装配,bean的id要与set方法保持一致\n> - 使用byType方式的自动装配,bean的类型要唯一,否则就会出错\n\n","slug":"Spring/9-bean自动装配","published":1,"updated":"2021-09-10T03:17:09.012Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pj0035akvehrre9dbw","content":"

bean自动装配

\"spring5\"

\n

代码

用一个项目来说明,如下:

\n

\"image-20210731164147058\"

\n

其中各个文件代码如下:

\n

Cat:

\n
1
2
3
4
5
6
7
package com.ajream.pojo;

public class Cat {
public void shout(){
System.out.println("miao~");
}
}
\n

Dog:

\n
1
2
3
4
5
6
7
8
package com.ajream.pojo;

public class Dog {
public void shout(){
System.out.println("wang~");
}
}

\n

Person:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.ajream.pojo;

public class Person {
private String name;
private Cat cat;
private Dog dog;

public String getName() {
return name;
}

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

public Cat getCat() {
return cat;
}

public void setCat(Cat cat) {
this.cat = cat;
}

public Dog getDog() {
return dog;
}

public void setDog(Dog dog) {
this.dog = dog;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}

}

\n

beans.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="dog" class="com.ajream.pojo.Dog"/>
<bean id="cat" class="com.ajream.pojo.Cat"/>
<bean id="person" class="com.ajream.pojo.Person">
<property name="name" value="张三"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
\n

MyTest:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.ajream.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class MyTest {

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person p = context.getBean("person", Person.class);

System.out.println(p.getName());
p.getCat().shout();
p.getDog().shout();

}

}

\n

分析

主要看 Person类和 beans.xml文件,在没有使用自动装配时

\n

\"image-20210731165352656\"

\n
    \n
  1. 使用 byName 自动装配
  2. \n
\n

\"image-20210731170102634\"

\n

使用自动装配就添加了 autowire = "byName" 这一配置,要求如下:

\n

\"image-20210731170646544\"

\n

byName就相当于nameref相同的情形(这也是一般情形),而由于name又与 setName 关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:

\n

\"image-20210731171212541\"

\n
    \n
  1. 使用 byType 自动装配
  2. \n
\n

对id名无要求,但要求bean类型唯一

\n

\"image-20210731171743101\"

\n

比如下面这种情况就会出错:

\n

\"image-20210731172014553\"

\n
\n

总结:

\n
    \n
  • 使用byName方式的自动装配,bean的id要与set方法保持一致
  • \n
  • 使用byType方式的自动装配,bean的类型要唯一,否则就会出错
  • \n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

bean自动装配

\"spring5\"

\n

代码

用一个项目来说明,如下:

\n

\"image-20210731164147058\"

\n

其中各个文件代码如下:

\n

Cat:

\n
1
2
3
4
5
6
7
package com.ajream.pojo;

public class Cat {
public void shout(){
System.out.println("miao~");
}
}
\n

Dog:

\n
1
2
3
4
5
6
7
8
package com.ajream.pojo;

public class Dog {
public void shout(){
System.out.println("wang~");
}
}

\n

Person:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.ajream.pojo;

public class Person {
private String name;
private Cat cat;
private Dog dog;

public String getName() {
return name;
}

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

public Cat getCat() {
return cat;
}

public void setCat(Cat cat) {
this.cat = cat;
}

public Dog getDog() {
return dog;
}

public void setDog(Dog dog) {
this.dog = dog;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}

}

\n

beans.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="dog" class="com.ajream.pojo.Dog"/>
<bean id="cat" class="com.ajream.pojo.Cat"/>
<bean id="person" class="com.ajream.pojo.Person">
<property name="name" value="张三"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
\n

MyTest:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.ajream.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class MyTest {

public static void main(String[] args) {

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person p = context.getBean("person", Person.class);

System.out.println(p.getName());
p.getCat().shout();
p.getDog().shout();

}

}

\n

分析

主要看 Person类和 beans.xml文件,在没有使用自动装配时

\n

\"image-20210731165352656\"

\n
    \n
  1. 使用 byName 自动装配
  2. \n
\n

\"image-20210731170102634\"

\n

使用自动装配就添加了 autowire = "byName" 这一配置,要求如下:

\n

\"image-20210731170646544\"

\n

byName就相当于nameref相同的情形(这也是一般情形),而由于name又与 setName 关联,所以这3个都要保持一致,实际就是代码运行时会自动转换成下面这种情况:

\n

\"image-20210731171212541\"

\n
    \n
  1. 使用 byType 自动装配
  2. \n
\n

对id名无要求,但要求bean类型唯一

\n

\"image-20210731171743101\"

\n

比如下面这种情况就会出错:

\n

\"image-20210731172014553\"

\n
\n

总结:

\n
    \n
  • 使用byName方式的自动装配,bean的id要与set方法保持一致
  • \n
  • 使用byType方式的自动装配,bean的类型要唯一,否则就会出错
  • \n
\n
\n"},{"title":"Spring(8)-命名空间和作用域","abbrlink":"3feecb5e","date":"2021-07-27T09:57:24.000Z","description":"spring的p、c命名空间使用,bean的作用域","cover":"https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png","_content":"\n\n# 命名空间和bean作用域\n\n\n\n## 命名空间\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master/)\n\n### p命名空间\n\n在beans中添加p命名空间的支持\n\n```xml\nxmlns:p=\"http://www.springframework.org/schema/p\"\n```\n\n\n\n以前要对一个属性进行注入,一般是按这种方式:\n\n```xml\n\n \n\n```\n\n使用了p命名空间,可以这样写:\n\n```xml\n\n```\n\np就是 `property` 的简写\n\n### c命名空间\n\n> c代表了 `constructor-arg`,因此c命名空间是通过构造函数来进行注入的\n\n在beans中添加c命名空间的支持\n\n```xml\nxmlns:c=\"http://www.springframework.org/schema/c\"\n```\n\n未使用命名空间:\n\n```xml\n\n \n\n```\n\n使用c命名空间:\n\n```xml\n\n```\n\n\n\n另外,p命名空间和c命名空间还可以一起使用\n\n\n\n## bean作用域\n\n\n\n### 单例模式\n\n(默认使用单例模式)\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/singleton.png)\n\n说明:\n\n```xml\n\n\n\n\n\n```\n\n用户每次从spring获取的bean,其hashcode是相同的, \n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2的hashcode相同\n```\n\n\n\n### 原生模式\n\n\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/prototype.png\"prototype\")\n\n```xml\n\n```\n\n用户每次从spring获取的bean,其hashcode是不同的\n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2 的 hashcode 不相同\n```\n\n\n\n\n\n\n\n","source":"_posts/Spring/8-命名空间和作用域.md","raw":"---\ntitle: Spring(8)-命名空间和作用域\ntags:\n - spring\ncategories:\n - - java\n - spring\nabbrlink: 3feecb5e\ndate: 2021-07-27 17:57:24\ndescription: spring的p、c命名空间使用,bean的作用域\ncover: https://gitee.com/ajream/images/raw/master/img/20210829232336_spring_cover.png\n---\n\n\n# 命名空间和bean作用域\n\n\n\n## 命名空间\n\n[项目地址](https://codechina.csdn.net/m0_46079750/spring5/-/tree/master/)\n\n### p命名空间\n\n在beans中添加p命名空间的支持\n\n```xml\nxmlns:p=\"http://www.springframework.org/schema/p\"\n```\n\n\n\n以前要对一个属性进行注入,一般是按这种方式:\n\n```xml\n\n \n\n```\n\n使用了p命名空间,可以这样写:\n\n```xml\n\n```\n\np就是 `property` 的简写\n\n### c命名空间\n\n> c代表了 `constructor-arg`,因此c命名空间是通过构造函数来进行注入的\n\n在beans中添加c命名空间的支持\n\n```xml\nxmlns:c=\"http://www.springframework.org/schema/c\"\n```\n\n未使用命名空间:\n\n```xml\n\n \n\n```\n\n使用c命名空间:\n\n```xml\n\n```\n\n\n\n另外,p命名空间和c命名空间还可以一起使用\n\n\n\n## bean作用域\n\n\n\n### 单例模式\n\n(默认使用单例模式)\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/singleton.png)\n\n说明:\n\n```xml\n\n\n\n\n\n```\n\n用户每次从spring获取的bean,其hashcode是相同的, \n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2的hashcode相同\n```\n\n\n\n### 原生模式\n\n\n\n![](https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/images/prototype.png\"prototype\")\n\n```xml\n\n```\n\n用户每次从spring获取的bean,其hashcode是不同的\n\n```java\nDefaultAccountService as1 = context.getBean(\"accountService\");\nDefaultAccountService as2 = context.getBean(\"accountService\");\n\n// as1与as2 的 hashcode 不相同\n```\n\n\n\n\n\n\n\n","slug":"Spring/8-命名空间和作用域","published":1,"updated":"2021-09-10T03:17:13.986Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pk0038akved9jw5nm3","content":"

命名空间和bean作用域

命名空间

项目地址

\n

p命名空间

在beans中添加p命名空间的支持

\n
1
xmlns:p="http://www.springframework.org/schema/p"
\n

以前要对一个属性进行注入,一般是按这种方式:

\n
1
2
3
<bean id="person" class="com.ajream.pojo.Person">
<property name="name" value="张三"/>
</bean>
\n

使用了p命名空间,可以这样写:

\n
1
<bean id="person" class="com.ajream.pojo.Person" p:name="张三" />
\n

p就是 property 的简写

\n

c命名空间

\n

c代表了 constructor-arg,因此c命名空间是通过构造函数来进行注入的

\n
\n

在beans中添加c命名空间的支持

\n
1
xmlns:c="http://www.springframework.org/schema/c"
\n

未使用命名空间:

\n
1
2
3
<bean id="person" class="com.ajream.pojo.Person">
<constructor-arg name="name" value="张三" />
</bean>
\n

使用c命名空间:

\n
1
<bean id="person" class="com.ajream.pojo.Person" c:name="王五" />
\n

另外,p命名空间和c命名空间还可以一起使用

\n

bean作用域

单例模式

(默认使用单例模式)

\n

\"\"

\n

说明:

\n
1
2
3
4
5
<!--下面这两行代码等效,因为默认是单例(singleton)的 -->

<bean id="accountService" class="com.something.DefaultAccountService"/>

<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
\n

用户每次从spring获取的bean,其hashcode是相同的,

\n
1
2
3
4
DefaultAccountService as1 = context.getBean("accountService");
DefaultAccountService as2 = context.getBean("accountService");

// as1与as2的hashcode相同
\n

原生模式

\"\"

\n
1
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
\n

用户每次从spring获取的bean,其hashcode是不同的

\n
1
2
3
4
DefaultAccountService as1 = context.getBean("accountService");
DefaultAccountService as2 = context.getBean("accountService");

// as1与as2 的 hashcode 不相同
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

命名空间和bean作用域

命名空间

项目地址

\n

p命名空间

在beans中添加p命名空间的支持

\n
1
xmlns:p="http://www.springframework.org/schema/p"
\n

以前要对一个属性进行注入,一般是按这种方式:

\n
1
2
3
<bean id="person" class="com.ajream.pojo.Person">
<property name="name" value="张三"/>
</bean>
\n

使用了p命名空间,可以这样写:

\n
1
<bean id="person" class="com.ajream.pojo.Person" p:name="张三" />
\n

p就是 property 的简写

\n

c命名空间

\n

c代表了 constructor-arg,因此c命名空间是通过构造函数来进行注入的

\n
\n

在beans中添加c命名空间的支持

\n
1
xmlns:c="http://www.springframework.org/schema/c"
\n

未使用命名空间:

\n
1
2
3
<bean id="person" class="com.ajream.pojo.Person">
<constructor-arg name="name" value="张三" />
</bean>
\n

使用c命名空间:

\n
1
<bean id="person" class="com.ajream.pojo.Person" c:name="王五" />
\n

另外,p命名空间和c命名空间还可以一起使用

\n

bean作用域

单例模式

(默认使用单例模式)

\n

\"\"

\n

说明:

\n
1
2
3
4
5
<!--下面这两行代码等效,因为默认是单例(singleton)的 -->

<bean id="accountService" class="com.something.DefaultAccountService"/>

<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
\n

用户每次从spring获取的bean,其hashcode是相同的,

\n
1
2
3
4
DefaultAccountService as1 = context.getBean("accountService");
DefaultAccountService as2 = context.getBean("accountService");

// as1与as2的hashcode相同
\n

原生模式

\"\"

\n
1
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
\n

用户每次从spring获取的bean,其hashcode是不同的

\n
1
2
3
4
DefaultAccountService as1 = context.getBean("accountService");
DefaultAccountService as2 = context.getBean("accountService");

// as1与as2 的 hashcode 不相同
\n"},{"title":"mybatis的Mapper详解(三)","description":"Mapper配置","abbrlink":"f218ca42","date":"2021-08-25T11:12:46.000Z","top_img":null,"cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n## mapper.xml\n\n\n\n在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement\n\n\n\n因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据\n\n\n\n### parameterType: 参数数据类型\n\n- 基本数据类型,int, long, ...\n\n ```xml\n \n delete from t_acount where id = #{id}\n \n ```\n\n \n\n- String类型: `java.lang.String`\n\n ```\n \n ```\n\n- 包装类,如:`java.lang.Integer`, `java.lang.Long`...\n\n- 自定义类类型\n\n ```xml\n \n \tinsert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n ```\n\n \n\n\n\n\n\n方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 `param1`, `param2` ...或者 `arg0`, `arg1` ... 【不同mybatis版本写可能不同,要注意param下标是从1开始的】\n\n例如:\n\n这是方法:\n\n```java\npublic Account findByNameAndAge(name, age);\n```\n\n则mapper如下\n\n```xml\n\n\n```\n\n\n\n\n\n\n\n### resultType:返回类型\n\n用法与parameterType相同\n\n【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是`int`】\n\n- 基本数据类型\n- 包装类\n- String类型\n- 自定义类类型\n\n\n\n\n\n\n\n","source":"_posts/Mybatis/mybatis的Mapper详解(三).md","raw":"---\ntitle: mybatis的Mapper详解(三)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: Mapper配置\nabbrlink: f218ca42\ndate: 2021-08-25 19:12:46\ntop_img: \ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n## mapper.xml\n\n\n\n在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement\n\n\n\n因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据\n\n\n\n### parameterType: 参数数据类型\n\n- 基本数据类型,int, long, ...\n\n ```xml\n \n delete from t_acount where id = #{id}\n \n ```\n\n \n\n- String类型: `java.lang.String`\n\n ```\n \n ```\n\n- 包装类,如:`java.lang.Integer`, `java.lang.Long`...\n\n- 自定义类类型\n\n ```xml\n \n \tinsert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n ```\n\n \n\n\n\n\n\n方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 `param1`, `param2` ...或者 `arg0`, `arg1` ... 【不同mybatis版本写可能不同,要注意param下标是从1开始的】\n\n例如:\n\n这是方法:\n\n```java\npublic Account findByNameAndAge(name, age);\n```\n\n则mapper如下\n\n```xml\n\n\n```\n\n\n\n\n\n\n\n### resultType:返回类型\n\n用法与parameterType相同\n\n【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是`int`】\n\n- 基本数据类型\n- 包装类\n- String类型\n- 自定义类类型\n\n\n\n\n\n\n\n","slug":"Mybatis/mybatis的Mapper详解(三)","published":1,"updated":"2021-08-29T14:45:05.560Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pl003bakve3evtamml","content":"

mapper.xml

在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement

\n

因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据

\n

parameterType: 参数数据类型

    \n
  • 基本数据类型,int, long, …

    \n
    1
    2
    3
    <delete id="deleteById" parameterType="int">
    delete from t_acount where id = #{id}
    </delete>
    \n
  • \n
\n
    \n
  • String类型: java.lang.String

    \n
    1
    2
    3
    <select id="findByName" resultType="com.ajream.entity.Account" parameterType="java.lang.String">
    select * from t_acount where username=#{username}
    </select>
    \n
  • \n
  • 包装类,如:java.lang.Integerjava.lang.Long

    \n
  • \n
  • 自定义类类型

    \n
    1
    2
    3
    <insert id="save" parameterType="com.ajream.entity.Account">
    \tinsert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})
    </insert>
    \n
  • \n
\n

方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 param1, param2 …或者 arg0, arg1 … 【不同mybatis版本写可能不同,要注意param下标是从1开始的】

\n

例如:

\n

这是方法:

\n
1
public Account findByNameAndAge(name, age);
\n

则mapper如下

\n
1
2
3
4

<select id="findByNameAndAge" resultType="com.ajream.entity.Account">
select * from t_acount where username=#{param1} and age=#{param2}
</select>
\n

resultType:返回类型

用法与parameterType相同

\n

【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是int

\n
    \n
  • 基本数据类型
  • \n
  • 包装类
  • \n
  • String类型
  • \n
  • 自定义类类型
  • \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

mapper.xml

在jdbc中,增(insert)、删(delete)、查(select)、改(update)统称为statement

\n

因此,在mapper中,statement包含这4种标签,在相应的标签内可以使用相应的SQL语句操作数据

\n

parameterType: 参数数据类型

    \n
  • 基本数据类型,int, long, …

    \n
    1
    2
    3
    <delete id="deleteById" parameterType="int">
    delete from t_acount where id = #{id}
    </delete>
    \n
  • \n
\n
    \n
  • String类型: java.lang.String

    \n
    1
    2
    3
    <select id="findByName" resultType="com.ajream.entity.Account" parameterType="java.lang.String">
    select * from t_acount where username=#{username}
    </select>
    \n
  • \n
  • 包装类,如:java.lang.Integerjava.lang.Long

    \n
  • \n
  • 自定义类类型

    \n
    1
    2
    3
    <insert id="save" parameterType="com.ajream.entity.Account">
    \tinsert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})
    </insert>
    \n
  • \n
\n

方法中有多个参数,不用写 parameterType,获取参数时不能用参数名,要用下标 param1, param2 …或者 arg0, arg1 … 【不同mybatis版本写可能不同,要注意param下标是从1开始的】

\n

例如:

\n

这是方法:

\n
1
public Account findByNameAndAge(name, age);
\n

则mapper如下

\n
1
2
3
4

<select id="findByNameAndAge" resultType="com.ajream.entity.Account">
select * from t_acount where username=#{param1} and age=#{param2}
</select>
\n

resultType:返回类型

用法与parameterType相同

\n

【增删改默认返回int类型,表示影响的行数,不需要指定返回类型是int

\n
    \n
  • 基本数据类型
  • \n
  • 包装类
  • \n
  • String类型
  • \n
  • 自定义类类型
  • \n
\n"},{"title":"mybatis动态SQL(七)","description":"mybatis动态生成sql语句","abbrlink":"6e81c43e","date":"2021-08-27T10:23:10.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。\n\n那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。\n\n\n\n对于这样一张表,要根据 `id/username/passwd/age`来选出指定的一条记录,用sql语句可能会这样写:\n\n```sql\nselect * from t_acount\nwhere id=7 and username=\"xiaoBai\" and passwd=\"12345xb\" and age=24;\n```\n\n![image-20210828220529310](https://gitee.com/ajream/images/raw/master/img/20210828220537_image-20210828220529310.png)\n\n\n\n在Java中需要这样:\n\n接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\npublic interface AccountDao {\n Account findByAccount(Account account);\n}\n\n```\n\n\n\n在mapper文件中:\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class AccountDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n Account account = new Account();\n account.setId(7);\n account.setAge(24);\n account.setName(\"xiaoBai\");\n account.setPasswd(\"12345xb\");\n\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n Account account1 = accountDao.findByAccount(account);\n System.out.println(account1);\n }\n}\n\n```\n\n\n\n假如,在下面这句代码中\n\n```xml\n\n```\n\n\n\n某一个属性如`passwd` 值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式\n\n\n\n\n\n### if标签\n\n```xml\n\n```\n\n\n\n\n\n这样写我们可以看到,如果 `passwd` 等于 null,那么查询语句为:\n\n```\nselect * from t_acount where id=#{id} and username=#{name} and age=#{age}\n```\n\n但是如果 `id` 为空呢?那么查询语句为:\n\nselect * from t_acount where and username=#{name} and age=#{age}\n\n这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句\n\n\n\n### where/if标签\n\n```xml\n\n```\n\n\n\n这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个`where`,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。\n\n\n\n\n\n### choose/when-otherwise标签\n\n有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句\n\n```xml\n\n```\n\n\n\n也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件\n\n- 如果 id 不为空,那么查询语句为:\n\n ```\n select * from t_acount where id=#{id}\n ```\n\n \n\n- 如果 id 为空,那么看name 是否为空,如果不为空,那么语句为:\n\n ```\n select * from user where username=#{name}\n ```\n\n- 如果 username 为空,passwd不为空,那么查询语句为:\n\n ```\n select * from user where passwd=#{passwd}\n ```\n\n- 如果前面3个均为空,那么查询语句为:\n\n ```\n select * from user where age=#{age}\n ```\n\n \n\n\n\n\n\n### trim标签\n\n\n\ntrim标记是一个格式化的标记,可以完成set或者是where标记的功能\n\n用 trim 改写前面的 where-if 语句\n\n```xml\n\n```\n\nprefix: 表示添加前缀 `where`\n\nprefixOverrides: 去掉第一个`and`或者是`or`\n\n\n\n### set/if标签\n\n\n\n```xml\n\n update t_acount\n \n \n username = #{name},\n \n \n passwd=#{passwd}\n \n \n \n where id=#{id}\n\n```\n\n\n\n这样写,如果第一个条件 name 为空,那么 sql 语句为\n\n```\nupdate t_acount set passwd=#{passwd} where id=#{id}\n```\n\n如果第两个条件都不为空,那么 sql 语句为\n\n```\nupdate t_acount set username=#{name}, passwd=#{passwd} where id=#{id}\n```\n\n\n\n\n\n### sql(片段)标签\n\n\n\n有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。\n\n比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:\n\n```xml\n\n\n \n AND username = #{name}\n \n \n AND passwd = #{passwd}\n \n\n```\n\n\n\n引用sql片段\n\n\n\n```xml\n\n```\n\n\n\n\n\n### foreach标签\n\n 需求:我们需要查询 user 表中 id 分别为1,2,3的用户\n\n sql语句:\n\n```sql\nselect * from user where id=1 or id=2 or id=3;\nselect * from user where id in (1,2,3);\n```\n\n\n\n建立一个 UserVo 类,里面封装一个 `List ids` 的属性\n\n\n\n```java\npackage com.ajream.entity;\n \nimport java.util.List;\nimport lombok.Data;\n\n@Data\npublic class UserVo {\n //封装多个用户的id\n private List ids;\n \n}  \n```\n\n用 foreach 来改写 `select * from user where id=1 or id=2 or id=3`\n\n\n\n```xml\n\n```\n\n\n\n\n\n用 foreach 来改写 `select * from user where id in (1,2,3)`\n\n```xml\n\n```\n\n\n\n\n\n总结:\n\n动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。\n\n\n\n\n\n","source":"_posts/Mybatis/mybatis动态sql(七).md","raw":"---\ntitle: mybatis动态SQL(七)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: mybatis动态生成sql语句\nabbrlink: 6e81c43e\ndate: 2021-08-27 18:23:10\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。\n\n那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。\n\n\n\n对于这样一张表,要根据 `id/username/passwd/age`来选出指定的一条记录,用sql语句可能会这样写:\n\n```sql\nselect * from t_acount\nwhere id=7 and username=\"xiaoBai\" and passwd=\"12345xb\" and age=24;\n```\n\n![image-20210828220529310](https://gitee.com/ajream/images/raw/master/img/20210828220537_image-20210828220529310.png)\n\n\n\n在Java中需要这样:\n\n接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\npublic interface AccountDao {\n Account findByAccount(Account account);\n}\n\n```\n\n\n\n在mapper文件中:\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class AccountDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n Account account = new Account();\n account.setId(7);\n account.setAge(24);\n account.setName(\"xiaoBai\");\n account.setPasswd(\"12345xb\");\n\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n Account account1 = accountDao.findByAccount(account);\n System.out.println(account1);\n }\n}\n\n```\n\n\n\n假如,在下面这句代码中\n\n```xml\n\n```\n\n\n\n某一个属性如`passwd` 值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式\n\n\n\n\n\n### if标签\n\n```xml\n\n```\n\n\n\n\n\n这样写我们可以看到,如果 `passwd` 等于 null,那么查询语句为:\n\n```\nselect * from t_acount where id=#{id} and username=#{name} and age=#{age}\n```\n\n但是如果 `id` 为空呢?那么查询语句为:\n\nselect * from t_acount where and username=#{name} and age=#{age}\n\n这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句\n\n\n\n### where/if标签\n\n```xml\n\n```\n\n\n\n这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个`where`,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。\n\n\n\n\n\n### choose/when-otherwise标签\n\n有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句\n\n```xml\n\n```\n\n\n\n也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件\n\n- 如果 id 不为空,那么查询语句为:\n\n ```\n select * from t_acount where id=#{id}\n ```\n\n \n\n- 如果 id 为空,那么看name 是否为空,如果不为空,那么语句为:\n\n ```\n select * from user where username=#{name}\n ```\n\n- 如果 username 为空,passwd不为空,那么查询语句为:\n\n ```\n select * from user where passwd=#{passwd}\n ```\n\n- 如果前面3个均为空,那么查询语句为:\n\n ```\n select * from user where age=#{age}\n ```\n\n \n\n\n\n\n\n### trim标签\n\n\n\ntrim标记是一个格式化的标记,可以完成set或者是where标记的功能\n\n用 trim 改写前面的 where-if 语句\n\n```xml\n\n```\n\nprefix: 表示添加前缀 `where`\n\nprefixOverrides: 去掉第一个`and`或者是`or`\n\n\n\n### set/if标签\n\n\n\n```xml\n\n update t_acount\n \n \n username = #{name},\n \n \n passwd=#{passwd}\n \n \n \n where id=#{id}\n\n```\n\n\n\n这样写,如果第一个条件 name 为空,那么 sql 语句为\n\n```\nupdate t_acount set passwd=#{passwd} where id=#{id}\n```\n\n如果第两个条件都不为空,那么 sql 语句为\n\n```\nupdate t_acount set username=#{name}, passwd=#{passwd} where id=#{id}\n```\n\n\n\n\n\n### sql(片段)标签\n\n\n\n有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。\n\n比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:\n\n```xml\n\n\n \n AND username = #{name}\n \n \n AND passwd = #{passwd}\n \n\n```\n\n\n\n引用sql片段\n\n\n\n```xml\n\n```\n\n\n\n\n\n### foreach标签\n\n 需求:我们需要查询 user 表中 id 分别为1,2,3的用户\n\n sql语句:\n\n```sql\nselect * from user where id=1 or id=2 or id=3;\nselect * from user where id in (1,2,3);\n```\n\n\n\n建立一个 UserVo 类,里面封装一个 `List ids` 的属性\n\n\n\n```java\npackage com.ajream.entity;\n \nimport java.util.List;\nimport lombok.Data;\n\n@Data\npublic class UserVo {\n //封装多个用户的id\n private List ids;\n \n}  \n```\n\n用 foreach 来改写 `select * from user where id=1 or id=2 or id=3`\n\n\n\n```xml\n\n```\n\n\n\n\n\n用 foreach 来改写 `select * from user where id in (1,2,3)`\n\n```xml\n\n```\n\n\n\n\n\n总结:\n\n动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。\n\n\n\n\n\n","slug":"Mybatis/mybatis动态sql(七)","published":1,"updated":"2021-08-29T14:45:24.576Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pm003eakve82ox37f6","content":"

前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。

\n

那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。

\n

对于这样一张表,要根据 id/username/passwd/age来选出指定的一条记录,用sql语句可能会这样写:

\n
1
2
select * from t_acount
where id=7 and username="xiaoBai" and passwd="12345xb" and age=24;
\n

\"image-20210828220529310\"

\n

在Java中需要这样:

\n

接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Account;

public interface AccountDao {
Account findByAccount(Account account);
}

\n

在mapper文件中:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ajream.dao.AccountDao">
<resultMap id="resultAccount" type="com.ajream.entity.Account">
<id column="id" property="id"/>
<result column="passwd" property="passwd"/>
<result column="username" property="name"/>
<result column="age" property="age"/>
</resultMap>
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}
</select>
</mapper>
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.ajream.test;

import com.ajream.dao.AccountDao;
import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class AccountDaoTest {
public static void main(String[] args) {
InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

Account account = new Account();
account.setId(7);
account.setAge(24);
account.setName("xiaoBai");
account.setPasswd("12345xb");

AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

Account account1 = accountDao.findByAccount(account);
System.out.println(account1);
}
}

\n

假如,在下面这句代码中

\n
1
2
3
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
\tselect * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}
</select>
\n

某一个属性如passwd 值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式

\n

if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount where
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</select>
\n

这样写我们可以看到,如果 passwd 等于 null,那么查询语句为:

\n
1
select * from t_acount where id=#{id} and username=#{name} and age=#{age}
\n

但是如果 id 为空呢?那么查询语句为:

\n

select * from t_acount where and username=#{name} and age=#{age}

\n

这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句

\n

where/if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<where>
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</where>
</select>
\n

这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个where,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。

\n

choose/when-otherwise标签

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<where>
<choose>
<when test="id != 0">
\tid=#{id}
</when>
<when test="name !='' and name != null">
\tand username=#{name}
</when>
<when test="passwd!='' and passwd!=null">
\tand passwd=#{passwd}
</when>
<otherwise>
and age=#{age}
</otherwise>
</choose>
</where>
</select>
\n

也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件

\n
    \n
  • 如果 id 不为空,那么查询语句为:

    \n
    1
    select * from t_acount where id=#{id}
    \n
  • \n
\n
    \n
  • 如果 id 为空,那么看name 是否为空,如果不为空,那么语句为:

    \n
    1
    select * from user where username=#{name}
    \n
  • \n
  • 如果 username 为空,passwd不为空,那么查询语句为:

    \n
    1
    select * from user where passwd=#{passwd}
    \n
  • \n
  • 如果前面3个均为空,那么查询语句为:

    \n
    1
    select * from user where age=#{age}
    \n
  • \n
\n

trim标签

trim标记是一个格式化的标记,可以完成set或者是where标记的功能

\n

用 trim 改写前面的 where-if 语句

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<trim prefix="where" prefixOverrides="and | or">
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</trim>
</select>
\n

prefix: 表示添加前缀 where

\n

prefixOverrides: 去掉第一个and或者是or

\n

set/if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
<update id="update" parameterType="com.ajream.entity.Account">
update t_acount
<set>
<if test="name != null and username != ''">
username = #{name},
</if>
<if test="passwd != null and passwd != ''">
passwd=#{passwd}
</if>
</set>

where id=#{id}
</update>
\n

这样写,如果第一个条件 name 为空,那么 sql 语句为

\n
1
update t_acount set passwd=#{passwd} where id=#{id}
\n

如果第两个条件都不为空,那么 sql 语句为

\n
1
update t_acount set username=#{name}, passwd=#{passwd} where id=#{id}
\n

sql(片段)标签

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

\n

比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:

\n
1
2
3
4
5
6
7
8
9
<!-- 定义 sql 片段 -->
<sql id="selectBySQL">
<if test="name != null and name != ''">
AND username = #{name}
</if>
<if test="passwd != null and passwd != ''">
AND passwd = #{passwd}
</if>
</sql>
\n

引用sql片段

\n
1
2
3
4
5
6
7
8
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<trim prefix="where" prefixOverrides="and | or">
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="selectBySQL"/>
<!-- 在这里还可以引用其他的 sql 片段 -->
</trim>
</select>
\n

foreach标签

 需求:我们需要查询 user 表中 id 分别为1,2,3的用户

\n

 sql语句:

\n
1
2
select * from user where id=1 or id=2 or id=3;
select * from user where id in (1,2,3);
\n

建立一个 UserVo 类,里面封装一个 List<Integer> ids 的属性

\n
1
2
3
4
5
6
7
8
9
10
11
package com.ajream.entity;

import java.util.List;
import lombok.Data;

@Data
public class UserVo {
//封装多个用户的id
private List<Integer> ids;

}  
\n

用 foreach 来改写 select * from user where id=1 or id=2 or id=3

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="selectUserByListId" parameterType="com.ajream.entity.UserVo" resultType="com.ajream.entity.User">
select * from user
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from user where 1=1 and (id=1 or id=2 or id=3)
-->
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
\n

用 foreach 来改写 select * from user where id in (1,2,3)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.ys.po.User">
select * from user
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from user where 1=1 and id in (1,2,3)
-->
<foreach collection="ids" item="id" open="and id in (" close=") " separator=",">
#{id}
</foreach>
</where>
</select>
\n

总结:

\n

动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

前面我们通过实例讲解了用mybatis对一张表进行的CRUD操作,但是我们发现写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL,稍微不注意,由于引号,空格等缺失可能都会导致错误。

\n

那么怎么去解决这个问题呢?这就是本篇所讲的使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。

\n

对于这样一张表,要根据 id/username/passwd/age来选出指定的一条记录,用sql语句可能会这样写:

\n
1
2
select * from t_acount
where id=7 and username="xiaoBai" and passwd="12345xb" and age=24;
\n

\"image-20210828220529310\"

\n

在Java中需要这样:

\n

接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Account;

public interface AccountDao {
Account findByAccount(Account account);
}

\n

在mapper文件中:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ajream.dao.AccountDao">
<resultMap id="resultAccount" type="com.ajream.entity.Account">
<id column="id" property="id"/>
<result column="passwd" property="passwd"/>
<result column="username" property="name"/>
<result column="age" property="age"/>
</resultMap>
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}
</select>
</mapper>
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.ajream.test;

import com.ajream.dao.AccountDao;
import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class AccountDaoTest {
public static void main(String[] args) {
InputStream inputStream = AccountDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

Account account = new Account();
account.setId(7);
account.setAge(24);
account.setName("xiaoBai");
account.setPasswd("12345xb");

AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

Account account1 = accountDao.findByAccount(account);
System.out.println(account1);
}
}

\n

假如,在下面这句代码中

\n
1
2
3
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
\tselect * from t_acount where id=#{id} and username=#{name} and passwd=#{passwd} and age=#{age}
</select>
\n

某一个属性如passwd 值为null,那么查询不到任何一条记录,为了避免这种情况,mybatis提供了动态生成sl语句的方式

\n

if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount where
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</select>
\n

这样写我们可以看到,如果 passwd 等于 null,那么查询语句为:

\n
1
select * from t_acount where id=#{id} and username=#{name} and age=#{age}
\n

但是如果 id 为空呢?那么查询语句为:

\n

select * from t_acount where and username=#{name} and age=#{age}

\n

这是错误的 SQL 语句,如何解决呢?请看下面的 where/if 语句

\n

where/if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<where>
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</where>
</select>
\n

这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个where,此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会剔除掉。

\n

choose/when-otherwise标签

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<where>
<choose>
<when test="id != 0">
\tid=#{id}
</when>
<when test="name !='' and name != null">
\tand username=#{name}
</when>
<when test="passwd!='' and passwd!=null">
\tand passwd=#{passwd}
</when>
<otherwise>
and age=#{age}
</otherwise>
</choose>
</where>
</select>
\n

也就是说,这里有4个条件:id, name, passwd, age,只能选择一个作为查询条件

\n
    \n
  • 如果 id 不为空,那么查询语句为:

    \n
    1
    select * from t_acount where id=#{id}
    \n
  • \n
\n
    \n
  • 如果 id 为空,那么看name 是否为空,如果不为空,那么语句为:

    \n
    1
    select * from user where username=#{name}
    \n
  • \n
  • 如果 username 为空,passwd不为空,那么查询语句为:

    \n
    1
    select * from user where passwd=#{passwd}
    \n
  • \n
  • 如果前面3个均为空,那么查询语句为:

    \n
    1
    select * from user where age=#{age}
    \n
  • \n
\n

trim标签

trim标记是一个格式化的标记,可以完成set或者是where标记的功能

\n

用 trim 改写前面的 where-if 语句

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<trim prefix="where" prefixOverrides="and | or">
<if test="id!=0">
id=#{id}
</if>
<if test="name!=null">
and username=#{name}
</if>
<if test="passwd!=null">
and passwd=#{passwd}
</if>
<if test="age!=0">
and age=#{age}
</if>
</trim>
</select>
\n

prefix: 表示添加前缀 where

\n

prefixOverrides: 去掉第一个and或者是or

\n

set/if标签

1
2
3
4
5
6
7
8
9
10
11
12
13
<update id="update" parameterType="com.ajream.entity.Account">
update t_acount
<set>
<if test="name != null and username != ''">
username = #{name},
</if>
<if test="passwd != null and passwd != ''">
passwd=#{passwd}
</if>
</set>

where id=#{id}
</update>
\n

这样写,如果第一个条件 name 为空,那么 sql 语句为

\n
1
update t_acount set passwd=#{passwd} where id=#{id}
\n

如果第两个条件都不为空,那么 sql 语句为

\n
1
update t_acount set username=#{name}, passwd=#{passwd} where id=#{id}
\n

sql(片段)标签

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

\n

比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:

\n
1
2
3
4
5
6
7
8
9
<!-- 定义 sql 片段 -->
<sql id="selectBySQL">
<if test="name != null and name != ''">
AND username = #{name}
</if>
<if test="passwd != null and passwd != ''">
AND passwd = #{passwd}
</if>
</sql>
\n

引用sql片段

\n
1
2
3
4
5
6
7
8
<select id="findByAccount" parameterType="com.ajream.entity.Account" resultMap="resultAccount">
select * from t_acount
<trim prefix="where" prefixOverrides="and | or">
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="selectBySQL"/>
<!-- 在这里还可以引用其他的 sql 片段 -->
</trim>
</select>
\n

foreach标签

 需求:我们需要查询 user 表中 id 分别为1,2,3的用户

\n

 sql语句:

\n
1
2
select * from user where id=1 or id=2 or id=3;
select * from user where id in (1,2,3);
\n

建立一个 UserVo 类,里面封装一个 List<Integer> ids 的属性

\n
1
2
3
4
5
6
7
8
9
10
11
package com.ajream.entity;

import java.util.List;
import lombok.Data;

@Data
public class UserVo {
//封装多个用户的id
private List<Integer> ids;

}  
\n

用 foreach 来改写 select * from user where id=1 or id=2 or id=3

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="selectUserByListId" parameterType="com.ajream.entity.UserVo" resultType="com.ajream.entity.User">
select * from user
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from user where 1=1 and (id=1 or id=2 or id=3)
-->
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
\n

用 foreach 来改写 select * from user where id in (1,2,3)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.ys.po.User">
select * from user
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from user where 1=1 and id in (1,2,3)
-->
<foreach collection="ids" item="id" open="and id in (" close=") " separator=",">
#{id}
</foreach>
</where>
</select>
\n

总结:

\n

动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。

\n"},{"title":"mybatis延迟加载-缓存机制(六)","description":"mybatis通过延迟加载、缓存机制来提高程序运行效率","abbrlink":"3b32a20d","date":"2021-08-27T07:43:16.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n\n\n## 延迟加载\n\nMyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。\n\nMyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。\n\n\n\n### 加载时机\n\n直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。\n\n侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。\n\n深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。\n\n{% note info flat %}\n\n注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。\n因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。\n\n{% endnote %}\n\n\n### 侵入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载\n\n ```xml\n \n \n \t\n \n ```\n2. 不调用主加载对象时只有一条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231810.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231811.png)\n\n3. 调用主加载对象的信息时会产生两条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231812.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231813.png)\n\n\n### 深入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载\n\n ```xml\n \n \n \n \n ```\n2. 调用主加载对象时不会执行第二条加载SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231814.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231915.png)\n\n3. 调用关联对象详细信息时会执行第二次查询\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231916.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231917.png)\n\n\n\n\n\n\n\n## 缓存\n\n\n\nMyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。\n\nMyBatis提供一级缓存和二级缓存的机制。\n\n\n\n### 一级缓存\n\n一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。\n\n当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。\n\n\n\n### 二级缓存\n\n二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件中的sql语句,多个SqlSession类的实例对象可以共用二级缓存\n\n二级缓存是跨SqlSession的。\n\n一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。\n\n开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true\n\n```xml\n \n \n \n```\n\n\n\n![image-20210829001744654](https://gitee.com/ajream/images/raw/master/img/20210829001748_image-20210829001744654.png)\n\n\n\nMyBatis的缓存模式如图所示:\n\n\n\n![image-20210829001805748](https://gitee.com/ajream/images/raw/master/img/20210829001807_image-20210829001805748.png)\n\n\n\n\n\n注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。\n\n","source":"_posts/Mybatis/mybatis延迟加载(六).md","raw":"---\ntitle: mybatis延迟加载-缓存机制(六)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: mybatis通过延迟加载、缓存机制来提高程序运行效率\nabbrlink: 3b32a20d\ndate: 2021-08-27 15:43:16\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n\n\n## 延迟加载\n\nMyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。\n\nMyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。\n\n\n\n### 加载时机\n\n直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。\n\n侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。\n\n深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。\n\n{% note info flat %}\n\n注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。\n因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。\n\n{% endnote %}\n\n\n### 侵入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载\n\n ```xml\n \n \n \t\n \n ```\n2. 不调用主加载对象时只有一条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231810.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231811.png)\n\n3. 调用主加载对象的信息时会产生两条SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231812.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231813.png)\n\n\n### 深入式延迟加载\n\n1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载\n\n ```xml\n \n \n \n \n ```\n2. 调用主加载对象时不会执行第二条加载SQL\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231814.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231915.png)\n\n3. 调用关联对象详细信息时会执行第二次查询\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231916.png)\n\n ![img](https://img.jbzj.com/file_images/article/201910/2019102414231917.png)\n\n\n\n\n\n\n\n## 缓存\n\n\n\nMyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。\n\nMyBatis提供一级缓存和二级缓存的机制。\n\n\n\n### 一级缓存\n\n一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。\n\n当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。\n\n\n\n### 二级缓存\n\n二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件中的sql语句,多个SqlSession类的实例对象可以共用二级缓存\n\n二级缓存是跨SqlSession的。\n\n一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。\n\n开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true\n\n```xml\n \n \n \n```\n\n\n\n![image-20210829001744654](https://gitee.com/ajream/images/raw/master/img/20210829001748_image-20210829001744654.png)\n\n\n\nMyBatis的缓存模式如图所示:\n\n\n\n![image-20210829001805748](https://gitee.com/ajream/images/raw/master/img/20210829001807_image-20210829001805748.png)\n\n\n\n\n\n注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。\n\n","slug":"Mybatis/mybatis延迟加载(六)","published":1,"updated":"2021-08-30T13:49:53.564Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pn003hakvebel7gurk","content":"

延迟加载

MyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。

\n

MyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。

\n

加载时机

直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。

\n

侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。

\n

深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。

\n

注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。
因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。

\n
\n

侵入式延迟加载

    \n
  1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载

    \n
    1
    2
    3
    4
    5
    6
    7
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--配置侵入式延迟加载 默认为false(深度加载)
    侵入式:默认只会执行主加载SQL,那么当访问主加载对象的详细信息时才会执行关联对象的SQL查询
    深度延迟:默认只执行主加载SQL,那么当调用到主加载对象中关联对象的信息时才会执行关联对象的SQL查询
    -->\t
    <setting name="aggressiveLazyLoading" value="true"/>
  2. \n
  3. 不调用主加载对象时只有一条SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  4. \n
  5. 调用主加载对象的信息时会产生两条SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  6. \n
\n

深入式延迟加载

    \n
  1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载

    \n
    1
    2
    3
    4
    5
    6
    7
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--配置侵入式延迟加载 默认为false(深度加载)
    侵入式:默认只会执行主加载SQL,那么当访问主加载对象的详细信息时才会执行关联对象的SQL查询
    深度延迟:默认只执行主加载SQL,那么当调用到主加载对象中关联对象的信息时才会执行关联对象的SQL查询
    -->
    <setting name="aggressiveLazyLoading" value="false"/>
  2. \n
  3. 调用主加载对象时不会执行第二条加载SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  4. \n
  5. 调用关联对象详细信息时会执行第二次查询

    \n

    \"img\"

    \n

    \"img\"

    \n
  6. \n
\n

缓存

MyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。

\n

MyBatis提供一级缓存和二级缓存的机制。

\n

一级缓存

一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。

\n

当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。

\n

二级缓存

二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件中的sql语句,多个SqlSession类的实例对象可以共用二级缓存

\n

二级缓存是跨SqlSession的。

\n

一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。

\n

开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true

\n
1
2
3
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
\n

\"image-20210829001744654\"

\n

MyBatis的缓存模式如图所示:

\n

\"image-20210829001805748\"

\n

注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

延迟加载

MyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。

\n

MyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。

\n

加载时机

直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。

\n

侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。

\n

深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。

\n

注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。
因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。

\n
\n

侵入式延迟加载

    \n
  1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置侵入式加载

    \n
    1
    2
    3
    4
    5
    6
    7
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--配置侵入式延迟加载 默认为false(深度加载)
    侵入式:默认只会执行主加载SQL,那么当访问主加载对象的详细信息时才会执行关联对象的SQL查询
    深度延迟:默认只执行主加载SQL,那么当调用到主加载对象中关联对象的信息时才会执行关联对象的SQL查询
    -->\t
    <setting name="aggressiveLazyLoading" value="true"/>
  2. \n
  3. 不调用主加载对象时只有一条SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  4. \n
  5. 调用主加载对象的信息时会产生两条SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  6. \n
\n

深入式延迟加载

    \n
  1. Mybatis-config.xml大配置文件,首先开启延迟加载,然后再配置深度加载

    \n
    1
    2
    3
    4
    5
    6
    7
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--配置侵入式延迟加载 默认为false(深度加载)
    侵入式:默认只会执行主加载SQL,那么当访问主加载对象的详细信息时才会执行关联对象的SQL查询
    深度延迟:默认只执行主加载SQL,那么当调用到主加载对象中关联对象的信息时才会执行关联对象的SQL查询
    -->
    <setting name="aggressiveLazyLoading" value="false"/>
  2. \n
  3. 调用主加载对象时不会执行第二条加载SQL

    \n

    \"img\"

    \n

    \"img\"

    \n
  4. \n
  5. 调用关联对象详细信息时会执行第二次查询

    \n

    \"img\"

    \n

    \"img\"

    \n
  6. \n
\n

缓存

MyBatis拥有自己的缓存结构,可以用来缓解数据库压力,加快查询速度。

\n

MyBatis提供一级缓存和二级缓存的机制。

\n

一级缓存

一级缓存是SqlSession级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时,每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的SqlSession类的实例对象缓存的数据区域(HashMap)是互不影响的。

\n

当在同一个SqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行sql直接从内存中获取。

\n

二级缓存

二级缓存是Mapper级别的缓存,多个SqlSession类的实例对象操作同一个Mapper配置文件中的sql语句,多个SqlSession类的实例对象可以共用二级缓存

\n

二级缓存是跨SqlSession的。

\n

一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。

\n

开启二级缓存:要开启二级缓存需要在mybatis配置文件中设置cacheEnabled属性为true

\n
1
2
3
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
\n

\"image-20210829001744654\"

\n

MyBatis的缓存模式如图所示:

\n

\"image-20210829001805748\"

\n

注意:有些时候在web工程中会将执行查询操作的方法封装在某个Service方法中,当查询万一次后,Service方法结束,此时SqlSession类的实例对下岗就会关闭,一级缓存就会被清空,测试若再次调用Service方法查询同一个信息,新打开一个SqlSession类的实例对象,由于一级缓存区域是空的,因而无法从缓存中获取信息;出现这种情况时,就可以使用二级缓存。

\n"},{"title":"mybatis介绍与使用(一)","date":"2021-08-25T02:30:46.000Z","abbrlink":"78903d87","description":"关于mybatis的简介与使用(第一个Mybatis程序)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n## 1、简介\n\n### 什么是 MyBatis\n\n​\tMyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。\n\n\n\n\n\n## 2、第一个MyBatis项目\n\n[项目地址mybatis-01](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master/)\n\n### (1)安装库\n\n​\t如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:\n\n```xml\n\n mysql\n mysql-connector-java\n 8.0.26\n \n\n\n\n org.mybatis\n mybatis\n 3.5.6\n\n\n\n\n junit\n junit\n 4.13\n test\n\n```\n\n### (2)创建MyBatis工具类\n\n为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据\n\ncom/ajream/utils/MybatisUtil.java\n\n```java\npackage com.ajream.utils;\n\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class MybatisUtil {\n\n private static SqlSessionFactory sqlSessionFactory;\n\n static {\n try {\n //mybatis-config.xml是mybatis配置文件,用于配置MySQL信息\n String resource = \"mybatis-config.xml\"; \n InputStream inputStream = Resources.getResourceAsStream(resource);\n sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);\n }catch (IOException e){\n e.printStackTrace();\n }\n }\n \n //生成Mysql的会话\n public static SqlSession getSqlSesion(){\n return sqlSessionFactory.openSession(); \n }\n\n}\n\n```\n\n\n\n配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)\n\n```xml\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n### (3)存放数据的容器类\n\n这是某个数据库school中student表中的数据\n\n![image-20210813000155615](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-01-58_image-20210813000155615.png)\n\n\n\n所以这里创建一个User类来表示数据库中的字段\n\ncom/ajream/pojo/User.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String id;\n private String name;\n private int age;\n\n public User() {\n }\n\n public User(String id, String name, int age) {\n this.id = id;\n this.name = name;\n this.age = age;\n }\n\n public String getId() {\n return id;\n }\n\n public void setId(String id) {\n this.id = id;\n }\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public int getAge() {\n return age;\n }\n\n public void setAge(int age) {\n this.age = age;\n }\n \n @Override\n public String toString() {\n return \"User{\" +\n \"id='\" + id + '\\'' +\n \", name='\" + name + '\\'' +\n \", age=\" + age +\n '}';\n }\n}\n\n```\n\n\n\n### (4)创建一个接口\n\n(用于获取并存放拿到的数据)\n\ncom/ajream/dao/UserDao.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\n\nimport java.util.List;\n\npublic interface UserDao {\n List getUserList();\n}\n\n```\n\n\n\n\n\n### (5)实现这个接口\n\n(即用sql语句获取数据)\n\n通常我们会创建一个 `UserDaoImpl` 来实现这个接口,但使用mybatis可以通过配置文件来代理实现:\n\ncom/ajream/dao/UserMapper.xml\n\n```xml\n\n\n\n\n\n \n \n \n \n\n \n\n```\n\n> Mapper.xml用于将MySQL数据库与Java类相联系起来\n>\n> - insert标签表示插入操作\n> - select标签是查询操作\n> - update是修改操作\n> - delete标签是删除操作\n\n\n\n> 最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:\n>\n> ```xml\n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n### (6)测试\n\n以上配置完成后,可以写个test测试程序来取出数据库中的数据了\n\n\n\ncom/ajream/dao/UserDaoTest.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\nimport com.ajream.utils.MybatisUtil;\nimport org.apache.ibatis.session.SqlSession;\nimport org.junit.Test;\n\nimport java.util.List;\n\n\npublic class UserDaoTest {\n\n @Test\n public void userDaoTest(){\n\n //获取MySQL会话\n SqlSession sqlSession = MybatisUtil.getSqlSesion();\n \n UserDao userDao = sqlSession.getMapper(UserDao.class);\n \n List userList = userDao.getUserList();\n\n for(User u: userList){\n System.out.println(u);\n }\n\n sqlSession.close();\n }\n}\n\n```\n\n\n\n### (7)注意\n\nidea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:\n\n```xml\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\n运行结果:\n\n\n\n![image-20210813001800000](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-18-01_image-20210813001800000.png)\n\n","source":"_posts/Mybatis/mybatis简单介绍与使用(一).md","raw":"---\ntitle: mybatis介绍与使用(一)\ndate: 2021-08-25 10:30:46\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\nabbrlink: 78903d87\ndescription: 关于mybatis的简介与使用(第一个Mybatis程序)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n## 1、简介\n\n### 什么是 MyBatis\n\n​\tMyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。\n\n\n\n\n\n## 2、第一个MyBatis项目\n\n[项目地址mybatis-01](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master/)\n\n### (1)安装库\n\n​\t如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:\n\n```xml\n\n mysql\n mysql-connector-java\n 8.0.26\n \n\n\n\n org.mybatis\n mybatis\n 3.5.6\n\n\n\n\n junit\n junit\n 4.13\n test\n\n```\n\n### (2)创建MyBatis工具类\n\n为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据\n\ncom/ajream/utils/MybatisUtil.java\n\n```java\npackage com.ajream.utils;\n\nimport org.apache.ibatis.io.Resources;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class MybatisUtil {\n\n private static SqlSessionFactory sqlSessionFactory;\n\n static {\n try {\n //mybatis-config.xml是mybatis配置文件,用于配置MySQL信息\n String resource = \"mybatis-config.xml\"; \n InputStream inputStream = Resources.getResourceAsStream(resource);\n sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);\n }catch (IOException e){\n e.printStackTrace();\n }\n }\n \n //生成Mysql的会话\n public static SqlSession getSqlSesion(){\n return sqlSessionFactory.openSession(); \n }\n\n}\n\n```\n\n\n\n配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)\n\n```xml\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n\n\n### (3)存放数据的容器类\n\n这是某个数据库school中student表中的数据\n\n![image-20210813000155615](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-01-58_image-20210813000155615.png)\n\n\n\n所以这里创建一个User类来表示数据库中的字段\n\ncom/ajream/pojo/User.java\n\n```java\npackage com.ajream.pojo;\n\npublic class User {\n private String id;\n private String name;\n private int age;\n\n public User() {\n }\n\n public User(String id, String name, int age) {\n this.id = id;\n this.name = name;\n this.age = age;\n }\n\n public String getId() {\n return id;\n }\n\n public void setId(String id) {\n this.id = id;\n }\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n public int getAge() {\n return age;\n }\n\n public void setAge(int age) {\n this.age = age;\n }\n \n @Override\n public String toString() {\n return \"User{\" +\n \"id='\" + id + '\\'' +\n \", name='\" + name + '\\'' +\n \", age=\" + age +\n '}';\n }\n}\n\n```\n\n\n\n### (4)创建一个接口\n\n(用于获取并存放拿到的数据)\n\ncom/ajream/dao/UserDao.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\n\nimport java.util.List;\n\npublic interface UserDao {\n List getUserList();\n}\n\n```\n\n\n\n\n\n### (5)实现这个接口\n\n(即用sql语句获取数据)\n\n通常我们会创建一个 `UserDaoImpl` 来实现这个接口,但使用mybatis可以通过配置文件来代理实现:\n\ncom/ajream/dao/UserMapper.xml\n\n```xml\n\n\n\n\n\n \n \n \n \n\n \n\n```\n\n> Mapper.xml用于将MySQL数据库与Java类相联系起来\n>\n> - insert标签表示插入操作\n> - select标签是查询操作\n> - update是修改操作\n> - delete标签是删除操作\n\n\n\n> 最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:\n>\n> ```xml\n> \n> \n> \n> \n> ```\n>\n> \n\n\n\n\n\n### (6)测试\n\n以上配置完成后,可以写个test测试程序来取出数据库中的数据了\n\n\n\ncom/ajream/dao/UserDaoTest.java\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.pojo.User;\nimport com.ajream.utils.MybatisUtil;\nimport org.apache.ibatis.session.SqlSession;\nimport org.junit.Test;\n\nimport java.util.List;\n\n\npublic class UserDaoTest {\n\n @Test\n public void userDaoTest(){\n\n //获取MySQL会话\n SqlSession sqlSession = MybatisUtil.getSqlSesion();\n \n UserDao userDao = sqlSession.getMapper(UserDao.class);\n \n List userList = userDao.getUserList();\n\n for(User u: userList){\n System.out.println(u);\n }\n\n sqlSession.close();\n }\n}\n\n```\n\n\n\n### (7)注意\n\nidea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:\n\n```xml\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\n运行结果:\n\n\n\n![image-20210813001800000](https://gitee.com/ajream/images/raw/master/img/2021-08-13 00-18-01_image-20210813001800000.png)\n\n","slug":"Mybatis/mybatis简单介绍与使用(一)","published":1,"updated":"2021-08-29T14:44:46.760Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6po003kakveajnw0fjv","content":"

1、简介

什么是 MyBatis

​ MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

\n

2、第一个MyBatis项目

项目地址mybatis-01

\n

(1)安装库

​ 如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
<!--注意:由于本地系统安装了版本8的Mysql,所以这里使用版本8的驱动-->
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<!--junit是测试时用到的-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
\n

(2)创建MyBatis工具类

为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据

\n

com/ajream/utils/MybatisUtil.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.ajream.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MybatisUtil {

private static SqlSessionFactory sqlSessionFactory;

static {
try {
//mybatis-config.xml是mybatis配置文件,用于配置MySQL信息
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch (IOException e){
e.printStackTrace();
}
}

//生成Mysql的会话
public static SqlSession getSqlSesion(){
return sqlSessionFactory.openSession();
}

}

\n

配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置mybatis运行环境-->
<environments default="development">
<environment id="development">
<!--配置jdbc事务管理-->
<transactionManager type="JDBC"/>
<!--POOLED配置jdbc数据源连接池-->
<dataSource type="POOLED">

<!--注意value=”com.mysql.cj.jdbc.Driver“中的【cj】是MySQL版本8才要使用的-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>

<!--连接数据库用到的url,这里连接的database是school,根据自己情况修改-->
<property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>

<!--用户名和密码-->
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>

</dataSource>
</environment>
</environments>

<!--com/ajream/dao的路径下还有一个配置文件,此处把它包含进来-->
<mappers>
<mapper resource="com/ajream/dao/UserMapper.xml"/>
</mappers>

</configuration>
\n

(3)存放数据的容器类

这是某个数据库school中student表中的数据

\n

\"image-20210813000155615\"

\n

所以这里创建一个User类来表示数据库中的字段

\n

com/ajream/pojo/User.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.ajream.pojo;

public class User {
private String id;
private String name;
private int age;

public User() {
}

public User(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}

public String getId() {
return id;
}

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

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

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

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

\n

(4)创建一个接口

(用于获取并存放拿到的数据)

\n

com/ajream/dao/UserDao.java

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.dao;

import com.ajream.pojo.User;

import java.util.List;

public interface UserDao {
List<User> getUserList();
}

\n

(5)实现这个接口

(即用sql语句获取数据)

\n

通常我们会创建一个 UserDaoImpl 来实现这个接口,但使用mybatis可以通过配置文件来代理实现:

\n

com/ajream/dao/UserMapper.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--命名空间是接口名-->
<mapper namespace="com.ajream.dao.UserDao">

<!--getUserList是接口的方法名,returnType是方法返回的数据类型-->
<select id="getUserList" resultType="com.ajream.pojo.User">
select * from school.student
</select>

<!--还支持insert/update/delete标签

<insert id="..." parameterType="xxx" >
\tinsert into 表名() values()
</insert>
\t......
-->

</mapper>
\n
\n

Mapper.xml用于将MySQL数据库与Java类相联系起来

\n
    \n
  • insert标签表示插入操作
  • \n
  • select标签是查询操作
  • \n
  • update是修改操作
  • \n
  • delete标签是删除操作
  • \n
\n

最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:

\n
1
2
3
4
<mappers>
<mapper resource="com/ajream/dao/UserMapper.xml"/>
</mappers>

\n
\n

(6)测试

以上配置完成后,可以写个test测试程序来取出数据库中的数据了

\n

com/ajream/dao/UserDaoTest.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.ajream.dao;

import com.ajream.pojo.User;
import com.ajream.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;


public class UserDaoTest {

@Test
public void userDaoTest(){

//获取MySQL会话
SqlSession sqlSession = MybatisUtil.getSqlSesion();

UserDao userDao = sqlSession.getMapper(UserDao.class);

List<User> userList = userDao.getUserList();

for(User u: userList){
System.out.println(u);
}

sqlSession.close();
}
}

\n

(7)注意

idea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

运行结果:

\n

\"image-20210813001800000\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

1、简介

什么是 MyBatis

​ MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

\n

2、第一个MyBatis项目

项目地址mybatis-01

\n

(1)安装库

​ 如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
<!--注意:由于本地系统安装了版本8的Mysql,所以这里使用版本8的驱动-->
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<!--junit是测试时用到的-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
\n

(2)创建MyBatis工具类

为了获取数据库表中的数据,需要创建一个MyBatis工具类com.ajream.utils.MybatisUtil,用于生成Mysql的会话,通过这个会话,可以get到数据库表中的数据

\n

com/ajream/utils/MybatisUtil.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.ajream.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MybatisUtil {

private static SqlSessionFactory sqlSessionFactory;

static {
try {
//mybatis-config.xml是mybatis配置文件,用于配置MySQL信息
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch (IOException e){
e.printStackTrace();
}
}

//生成Mysql的会话
public static SqlSession getSqlSesion(){
return sqlSessionFactory.openSession();
}

}

\n

配置文件:mybatis-config.xml (用于配置连接MySQL的基本信息)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置mybatis运行环境-->
<environments default="development">
<environment id="development">
<!--配置jdbc事务管理-->
<transactionManager type="JDBC"/>
<!--POOLED配置jdbc数据源连接池-->
<dataSource type="POOLED">

<!--注意value=”com.mysql.cj.jdbc.Driver“中的【cj】是MySQL版本8才要使用的-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>

<!--连接数据库用到的url,这里连接的database是school,根据自己情况修改-->
<property name="url" value="jdbc:mysql://localhost:3306/school?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>

<!--用户名和密码-->
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>

</dataSource>
</environment>
</environments>

<!--com/ajream/dao的路径下还有一个配置文件,此处把它包含进来-->
<mappers>
<mapper resource="com/ajream/dao/UserMapper.xml"/>
</mappers>

</configuration>
\n

(3)存放数据的容器类

这是某个数据库school中student表中的数据

\n

\"image-20210813000155615\"

\n

所以这里创建一个User类来表示数据库中的字段

\n

com/ajream/pojo/User.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.ajream.pojo;

public class User {
private String id;
private String name;
private int age;

public User() {
}

public User(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}

public String getId() {
return id;
}

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

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

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

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

\n

(4)创建一个接口

(用于获取并存放拿到的数据)

\n

com/ajream/dao/UserDao.java

\n
1
2
3
4
5
6
7
8
9
10
package com.ajream.dao;

import com.ajream.pojo.User;

import java.util.List;

public interface UserDao {
List<User> getUserList();
}

\n

(5)实现这个接口

(即用sql语句获取数据)

\n

通常我们会创建一个 UserDaoImpl 来实现这个接口,但使用mybatis可以通过配置文件来代理实现:

\n

com/ajream/dao/UserMapper.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--命名空间是接口名-->
<mapper namespace="com.ajream.dao.UserDao">

<!--getUserList是接口的方法名,returnType是方法返回的数据类型-->
<select id="getUserList" resultType="com.ajream.pojo.User">
select * from school.student
</select>

<!--还支持insert/update/delete标签

<insert id="..." parameterType="xxx" >
\tinsert into 表名() values()
</insert>
\t......
-->

</mapper>
\n
\n

Mapper.xml用于将MySQL数据库与Java类相联系起来

\n
    \n
  • insert标签表示插入操作
  • \n
  • select标签是查询操作
  • \n
  • update是修改操作
  • \n
  • delete标签是删除操作
  • \n
\n

最后要记得在mybatis配置文件中注册mapper.xml文件,即在mybatis-config.xml文件添加:

\n
1
2
3
4
<mappers>
<mapper resource="com/ajream/dao/UserMapper.xml"/>
</mappers>

\n
\n

(6)测试

以上配置完成后,可以写个test测试程序来取出数据库中的数据了

\n

com/ajream/dao/UserDaoTest.java

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.ajream.dao;

import com.ajream.pojo.User;
import com.ajream.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;


public class UserDaoTest {

@Test
public void userDaoTest(){

//获取MySQL会话
SqlSession sqlSession = MybatisUtil.getSqlSesion();

UserDao userDao = sqlSession.getMapper(UserDao.class);

List<User> userList = userDao.getUserList();

for(User u: userList){
System.out.println(u);
}

sqlSession.close();
}
}

\n

(7)注意

idea中只能读取到resources中的xml文件,而”Java“文件夹中的xml文件不能读取到,所以运行上面的测试会报错,要在pom.xml文件中添加如下配置:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

运行结果:

\n

\"image-20210813001800000\"

\n"},{"title":"mybatis介绍与使用(二)","description":"(一)的例子看不懂可以先看这个,也是一个入门mybatis的例子,该例子逻辑可能更清晰","abbrlink":"ab69c7ec","date":"2021-08-25T06:30:46.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n[项目地址mybatis-02](https://codechina.csdn.net/m0_46079750/mybatis-study)\n\n### 新建数据库表\n\n\n\n```sql\nUSE mybatis;\n\nCREATE TABLE t_acount(\n\tid INT PRIMARY KEY auto_increment,\n\tusername VARCHAR(11),\n\tpasswd VARCHAR(11),\n\tage INT\n)\n```\n\n\n\n### 配置mybatis开发环境\n\n\n\n使用maven创建新项目,在pom.xml导入依赖:\n\n```xml\n\n \n mysql\n mysql-connector-java\n 8.0.26\n \n\n \n org.mybatis\n mybatis\n 3.5.6\n \n\n \n junit\n junit\n 4.13\n test\n \n\n \n org.projectlombok\n lombok\n RELEASE\n compile\n \n\n```\n\n\n\n新建数据表对应的Account实体类\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n//@Data可以为类提供读写功能,从而不用写get、set方法; 会为类提供 equals()、hashCode()、toString() 方法。\n//@AllArgsConstructor 自动添加有参构造方法\n//@NoArgsConstructor 添加无参构造方法\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Account {\n private int id;\n private String username;\n private String passwd;\n private int age;\n}\n\n```\n\n\n\n在resources创建mybatis配置文件mybatis-config.xml\n\n首先要导入约束(注意这个与配置文件那个的是不同的)\n\n```xml\n\n\n```\n\nmybatis-config.xml:\n\n```xml\n\n\n\n\n\n\n \n \n\n \n\n \n\n \n \n \n \n \n\n \n \n\n```\n\n到此mybatis开发环境已经搭建好,接下来进行开发\n\n\n\n\n\n### Mybatis开发方式\n\n1. 原生接口方式\n2. mapper代理实现自定义接口方式\n\n\n\n\n\n> **先介绍第一种:原生接口方式**\n\n\n\nmybatis框架需要开发者自定义sql语句,写在Mapper.xml文件\n\n实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象\n\n\n\n\n\n(一)创建Mapper文件\n\n因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件\n\n\n\n![image-20210826131020613](https://gitee.com/ajream/images/raw/master/img/20210826131024_image-20210826131020613.png)\n\n\n\n\n\n首先也是要导入约束:\n\n```xml\n\n\n```\n\nAccountMapper.xml:\n\n```xml\n\n\n\n\n\n\n\n \n insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age})\n \n\n```\n\n说明:\n\n1. namespace通常设置为文件所在包名+文件名(无后缀)\n\n2. 可以把这个Mapper文件理解成一个java类,类名是 `AccountMapper`, 有一个方法 `save` ,可以传入参数,参数类型是 `com.ajream.entity.Account`\n\n 这个方法的作用是执行insert插入数据操作\n\n\n\n\n\n(二)注册Mapper文件\n\n在mybatis-config.xml配置文件中注册mapper文件, 添加:\n\n```xml\n\n\t\n\n```\n\n![image-20210826133054400](https://gitee.com/ajream/images/raw/master/img/20210826133057_image-20210826133054400.png)\n\n\n\n\n\n### 编写测试类进行运行\n\n1. 根据mybatis配置文件,使用工厂模式生成sqlSession\n2. 执行sql语句\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class MyTest {\n public static void main(String[] args) {\n //加载配置文件,获取sqlSession工厂对象\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis02-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n// 通过工厂对象获取sqlSession\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n String statement= \"com.ajream.mapper.AccountMapper.save\"; //用于加载mapper配置文件的save方法\n Account account = new Account(2,\"XiaoZhang\", \"56789xz\", 20);\n sqlSession.insert(statement, account); //执行sql语句\n sqlSession.commit(); //增、删、改 最后必须提交事务\n\n }\n}\n\n```\n\n\n\n\n\n\n\n注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件\n\n```xml\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\n\n\n如果报错:\n\n```\nCause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。\n```\n\n在pom.xml中添加语句:\n\n```xml\n\n UTF-8\n\n```\n\n![image-20210826150549895](https://gitee.com/ajream/images/raw/master/img/20210826150553_image-20210826150549895.png)\n\n\n\n\n\n\n\n> **第二种:Mapper代理实现自定义接口方式(推荐)**\n>\n> [项目地址mybatis-03](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master1)\n\n\n\n(一)创建接口:\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\nimport java.util.List;\n\n//增删改查\npublic interface AccountDao {\n int save(Account account);\n int update(Account account);\n int deleteById(int id);\n List findAll();\n Account findById(int id);\n}\n\n```\n\n\n\n\n\n(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过`insert/update/delete/select`标签来定义接口方法对应的sql语句\n\n\n\n规则:\n\n- namespace为接口的全类名\n\n- id为接口中对应的方法名\n\n- parameterType为接口中对应方法的参数类型\n\n- resultType为接口对应方法的返回类型\n\n 注意:1-如果方法返回的是集合,则resultType是里面元素的类型\n\n ​\t 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定\n\n\n\n```xml\n\n\n\n \n insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n \n update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id}\n \n \n delete from t_acount where id = #{id}\n \n\n \n\n \n\n\n\n```\n\n\n\n\n\n(三)注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n(四)测试\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\nimport java.util.List;\n\npublic class MyTest {\n public static void main(String[] args) {\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n// 实现接口的代理对象\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n// 增\n// accountDao.save(new Account(8, \"小叮当\", \"12345xdj\", 24));\n// sqlSession.commit(); //提交事务\n// 删\n// accountDao.deleteById(4);\n// sqlSession.commit(); //提交事务\n// 查\n// List list = accountDao.findAll();\n// for(Account account: list){\n// System.out.println(account);\n// }\n// Account account = accountDao.findById(2);\n// System.out.println(account);\n\n// 改(先查出要修改的对象,再修改对象的各个属性)\n Account account1 = accountDao.findById(1);\n account1.setUsername(\"小东\");\n account1.setAge(19);\n account1.setPasswd(\"56789xd\");\n\n int result = accountDao.update(account1);\n sqlSession.commit(); //提交事务\n System.out.println(\"修改行数:\" + result + \"修改后的对象:\");\n System.out.println(accountDao.findById(1));\n\n sqlSession.close(); //退出会话\n\n }\n}\n\n```\n\n\n\n这是进行测试的数据表\n\n\n\n![image-20210826203758449](https://gitee.com/ajream/images/raw/master/img/20210826203801_image-20210826203758449.png)\n\n\n\n\n\n","source":"_posts/Mybatis/mybatis简单使用(二).md","raw":"---\ntitle: mybatis介绍与使用(二)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: (一)的例子看不懂可以先看这个,也是一个入门mybatis的例子,该例子逻辑可能更清晰\nabbrlink: ab69c7ec\ndate: 2021-08-25 14:30:46\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n[项目地址mybatis-02](https://codechina.csdn.net/m0_46079750/mybatis-study)\n\n### 新建数据库表\n\n\n\n```sql\nUSE mybatis;\n\nCREATE TABLE t_acount(\n\tid INT PRIMARY KEY auto_increment,\n\tusername VARCHAR(11),\n\tpasswd VARCHAR(11),\n\tage INT\n)\n```\n\n\n\n### 配置mybatis开发环境\n\n\n\n使用maven创建新项目,在pom.xml导入依赖:\n\n```xml\n\n \n mysql\n mysql-connector-java\n 8.0.26\n \n\n \n org.mybatis\n mybatis\n 3.5.6\n \n\n \n junit\n junit\n 4.13\n test\n \n\n \n org.projectlombok\n lombok\n RELEASE\n compile\n \n\n```\n\n\n\n新建数据表对应的Account实体类\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n//@Data可以为类提供读写功能,从而不用写get、set方法; 会为类提供 equals()、hashCode()、toString() 方法。\n//@AllArgsConstructor 自动添加有参构造方法\n//@NoArgsConstructor 添加无参构造方法\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Account {\n private int id;\n private String username;\n private String passwd;\n private int age;\n}\n\n```\n\n\n\n在resources创建mybatis配置文件mybatis-config.xml\n\n首先要导入约束(注意这个与配置文件那个的是不同的)\n\n```xml\n\n\n```\n\nmybatis-config.xml:\n\n```xml\n\n\n\n\n\n\n \n \n\n \n\n \n\n \n \n \n \n \n\n \n \n\n```\n\n到此mybatis开发环境已经搭建好,接下来进行开发\n\n\n\n\n\n### Mybatis开发方式\n\n1. 原生接口方式\n2. mapper代理实现自定义接口方式\n\n\n\n\n\n> **先介绍第一种:原生接口方式**\n\n\n\nmybatis框架需要开发者自定义sql语句,写在Mapper.xml文件\n\n实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象\n\n\n\n\n\n(一)创建Mapper文件\n\n因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件\n\n\n\n![image-20210826131020613](https://gitee.com/ajream/images/raw/master/img/20210826131024_image-20210826131020613.png)\n\n\n\n\n\n首先也是要导入约束:\n\n```xml\n\n\n```\n\nAccountMapper.xml:\n\n```xml\n\n\n\n\n\n\n\n \n insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age})\n \n\n```\n\n说明:\n\n1. namespace通常设置为文件所在包名+文件名(无后缀)\n\n2. 可以把这个Mapper文件理解成一个java类,类名是 `AccountMapper`, 有一个方法 `save` ,可以传入参数,参数类型是 `com.ajream.entity.Account`\n\n 这个方法的作用是执行insert插入数据操作\n\n\n\n\n\n(二)注册Mapper文件\n\n在mybatis-config.xml配置文件中注册mapper文件, 添加:\n\n```xml\n\n\t\n\n```\n\n![image-20210826133054400](https://gitee.com/ajream/images/raw/master/img/20210826133057_image-20210826133054400.png)\n\n\n\n\n\n### 编写测试类进行运行\n\n1. 根据mybatis配置文件,使用工厂模式生成sqlSession\n2. 执行sql语句\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class MyTest {\n public static void main(String[] args) {\n //加载配置文件,获取sqlSession工厂对象\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis02-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n// 通过工厂对象获取sqlSession\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n String statement= \"com.ajream.mapper.AccountMapper.save\"; //用于加载mapper配置文件的save方法\n Account account = new Account(2,\"XiaoZhang\", \"56789xz\", 20);\n sqlSession.insert(statement, account); //执行sql语句\n sqlSession.commit(); //增、删、改 最后必须提交事务\n\n }\n}\n\n```\n\n\n\n\n\n\n\n注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件\n\n```xml\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\n\n\n如果报错:\n\n```\nCause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。\n```\n\n在pom.xml中添加语句:\n\n```xml\n\n UTF-8\n\n```\n\n![image-20210826150549895](https://gitee.com/ajream/images/raw/master/img/20210826150553_image-20210826150549895.png)\n\n\n\n\n\n\n\n> **第二种:Mapper代理实现自定义接口方式(推荐)**\n>\n> [项目地址mybatis-03](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master1)\n\n\n\n(一)创建接口:\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Account;\n\nimport java.util.List;\n\n//增删改查\npublic interface AccountDao {\n int save(Account account);\n int update(Account account);\n int deleteById(int id);\n List findAll();\n Account findById(int id);\n}\n\n```\n\n\n\n\n\n(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过`insert/update/delete/select`标签来定义接口方法对应的sql语句\n\n\n\n规则:\n\n- namespace为接口的全类名\n\n- id为接口中对应的方法名\n\n- parameterType为接口中对应方法的参数类型\n\n- resultType为接口对应方法的返回类型\n\n 注意:1-如果方法返回的是集合,则resultType是里面元素的类型\n\n ​\t 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定\n\n\n\n```xml\n\n\n\n \n insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})\n \n \n update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id}\n \n \n delete from t_acount where id = #{id}\n \n\n \n\n \n\n\n\n```\n\n\n\n\n\n(三)注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n(四)测试\n\n\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AccountDao;\nimport com.ajream.entity.Account;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\nimport java.util.List;\n\npublic class MyTest {\n public static void main(String[] args) {\n InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n// 实现接口的代理对象\n AccountDao accountDao = sqlSession.getMapper(AccountDao.class);\n\n// 增\n// accountDao.save(new Account(8, \"小叮当\", \"12345xdj\", 24));\n// sqlSession.commit(); //提交事务\n// 删\n// accountDao.deleteById(4);\n// sqlSession.commit(); //提交事务\n// 查\n// List list = accountDao.findAll();\n// for(Account account: list){\n// System.out.println(account);\n// }\n// Account account = accountDao.findById(2);\n// System.out.println(account);\n\n// 改(先查出要修改的对象,再修改对象的各个属性)\n Account account1 = accountDao.findById(1);\n account1.setUsername(\"小东\");\n account1.setAge(19);\n account1.setPasswd(\"56789xd\");\n\n int result = accountDao.update(account1);\n sqlSession.commit(); //提交事务\n System.out.println(\"修改行数:\" + result + \"修改后的对象:\");\n System.out.println(accountDao.findById(1));\n\n sqlSession.close(); //退出会话\n\n }\n}\n\n```\n\n\n\n这是进行测试的数据表\n\n\n\n![image-20210826203758449](https://gitee.com/ajream/images/raw/master/img/20210826203801_image-20210826203758449.png)\n\n\n\n\n\n","slug":"Mybatis/mybatis简单使用(二)","published":1,"updated":"2021-08-29T14:44:59.444Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pp003nakve3uzjgiks","content":"

项目地址mybatis-02

\n

新建数据库表

1
2
3
4
5
6
7
8
USE mybatis;

CREATE TABLE t_acount(
\tid INT PRIMARY KEY auto_increment,
\tusername VARCHAR(11),
\tpasswd VARCHAR(11),
\tage INT
)
\n

配置mybatis开发环境

使用maven创建新项目,在pom.xml导入依赖:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
\n

新建数据表对应的Account实体类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//@Data可以为类提供读写功能,从而不用写get、set方法; 会为类提供 equals()、hashCode()、toString() 方法。
//@AllArgsConstructor 自动添加有参构造方法
//@NoArgsConstructor 添加无参构造方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
private int id;
private String username;
private String passwd;
private int age;
}

\n

在resources创建mybatis配置文件mybatis-config.xml

\n

首先要导入约束(注意这个与配置文件那个的是不同的)

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
\n

mybatis-config.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!-- 2. 配置mybatis运行环境,可以有多个运行环境,default表示默认的运行环境是...,id是每个运行环境的唯一标识-->
<environments default="test1">
<environment id="test1">
<!-- 3. 配置JDBC事务管理-->
<transactionManager type="JDBC"></transactionManager>
<!-- 4. 配置JDBC数据源连接池POOLED-->
<dataSource type="POOLED">
<!-- 5. 配置数据库链接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--配置驱动-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>

</environment>
</environments>
</configuration>
\n

到此mybatis开发环境已经搭建好,接下来进行开发

\n

Mybatis开发方式

    \n
  1. 原生接口方式
  2. \n
  3. mapper代理实现自定义接口方式
  4. \n
\n
\n

先介绍第一种:原生接口方式

\n
\n

mybatis框架需要开发者自定义sql语句,写在Mapper.xml文件

\n

实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象

\n

(一)创建Mapper文件

\n

因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件

\n

\"image-20210826131020613\"

\n

首先也是要导入约束:

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
\n

AccountMapper.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--1. mapper标签,namespace是当前mapper文件路径-->
<mapper namespace="com.ajream.mapper.AccountMapper">
<!-- 2. 进行增删改查操作,id是后面要调用sql语句时用的的方法名,parameterType是方法中的参数的类型-->
<!-- #{id}等这些都是Account实体类中的属性,表示从中取出值-->
<insert id="save" parameterType="com.ajream.entity.Account">
insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age})
</insert>
</mapper>
\n

说明:

\n
    \n
  1. namespace通常设置为文件所在包名+文件名(无后缀)

    \n
  2. \n
  3. 可以把这个Mapper文件理解成一个java类,类名是 AccountMapper, 有一个方法 save ,可以传入参数,参数类型是 com.ajream.entity.Account

    \n

    这个方法的作用是执行insert插入数据操作

    \n
  4. \n
\n

(二)注册Mapper文件

\n

在mybatis-config.xml配置文件中注册mapper文件, 添加:

\n
1
2
3
<mappers>
\t<mapper resource="com/ajream/mapper/AccountMapper.xml"></mapper>
</mappers>
\n

\"image-20210826133054400\"

\n

编写测试类进行运行

    \n
  1. 根据mybatis配置文件,使用工厂模式生成sqlSession
  2. \n
  3. 执行sql语句
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.ajream.test;

import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class MyTest {
public static void main(String[] args) {
//加载配置文件,获取sqlSession工厂对象
InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis02-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 通过工厂对象获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

String statement= "com.ajream.mapper.AccountMapper.save"; //用于加载mapper配置文件的save方法
Account account = new Account(2,"XiaoZhang", "56789xz", 20);
sqlSession.insert(statement, account); //执行sql语句
sqlSession.commit(); //增、删、改 最后必须提交事务

}
}

\n

注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

如果报错:

\n
1
Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。
\n

在pom.xml中添加语句:

\n
1
2
3
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
\n

\"image-20210826150549895\"

\n
\n

第二种:Mapper代理实现自定义接口方式(推荐)

\n

项目地址mybatis-03

\n
\n

(一)创建接口:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.dao;

import com.ajream.entity.Account;

import java.util.List;

//增删改查
public interface AccountDao {
int save(Account account);
int update(Account account);
int deleteById(int id);
List<Account> findAll();
Account findById(int id);
}

\n

(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过insert/update/delete/select标签来定义接口方法对应的sql语句

\n

规则:

\n
    \n
  • namespace为接口的全类名

    \n
  • \n
  • id为接口中对应的方法名

    \n
  • \n
  • parameterType为接口中对应方法的参数类型

    \n
  • \n
  • resultType为接口对应方法的返回类型

    \n

    注意:1-如果方法返回的是集合,则resultType是里面元素的类型

    \n

    ​ 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ajream.dao.AccountDao">
<insert id="save" parameterType="com.ajream.entity.Account">
insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})
</insert>
<update id="update" parameterType="com.ajream.entity.Account">
update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id}
</update>
<delete id="deleteById" parameterType="int">
delete from t_acount where id = #{id}
</delete>

<select id="findAll" resultType="com.ajream.entity.Account">
select * from t_acount;
</select>

<select id="findById" resultType="com.ajream.entity.Account" parameterType="int">
select * from t_acount where id=#{id}
</select>
</mapper>


\n

(三)注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/AccountDao.xml" />
</mappers>
\n

(四)测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.ajream.test;

import com.ajream.dao.AccountDao;
import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;
import java.util.List;

public class MyTest {
public static void main(String[] args) {
InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

// 实现接口的代理对象
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

// 增
// accountDao.save(new Account(8, "小叮当", "12345xdj", 24));
// sqlSession.commit(); //提交事务
// 删
// accountDao.deleteById(4);
// sqlSession.commit(); //提交事务
// 查
// List<Account> list = accountDao.findAll();
// for(Account account: list){
// System.out.println(account);
// }
// Account account = accountDao.findById(2);
// System.out.println(account);

// 改(先查出要修改的对象,再修改对象的各个属性)
Account account1 = accountDao.findById(1);
account1.setUsername("小东");
account1.setAge(19);
account1.setPasswd("56789xd");

int result = accountDao.update(account1);
sqlSession.commit(); //提交事务
System.out.println("修改行数:" + result + "修改后的对象:");
System.out.println(accountDao.findById(1));

sqlSession.close(); //退出会话

}
}

\n

这是进行测试的数据表

\n

\"image-20210826203758449\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

项目地址mybatis-02

\n

新建数据库表

1
2
3
4
5
6
7
8
USE mybatis;

CREATE TABLE t_acount(
\tid INT PRIMARY KEY auto_increment,
\tusername VARCHAR(11),
\tpasswd VARCHAR(11),
\tage INT
)
\n

配置mybatis开发环境

使用maven创建新项目,在pom.xml导入依赖:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
\n

新建数据表对应的Account实体类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

//@Data可以为类提供读写功能,从而不用写get、set方法; 会为类提供 equals()、hashCode()、toString() 方法。
//@AllArgsConstructor 自动添加有参构造方法
//@NoArgsConstructor 添加无参构造方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {
private int id;
private String username;
private String passwd;
private int age;
}

\n

在resources创建mybatis配置文件mybatis-config.xml

\n

首先要导入约束(注意这个与配置文件那个的是不同的)

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
\n

mybatis-config.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!-- 2. 配置mybatis运行环境,可以有多个运行环境,default表示默认的运行环境是...,id是每个运行环境的唯一标识-->
<environments default="test1">
<environment id="test1">
<!-- 3. 配置JDBC事务管理-->
<transactionManager type="JDBC"></transactionManager>
<!-- 4. 配置JDBC数据源连接池POOLED-->
<dataSource type="POOLED">
<!-- 5. 配置数据库链接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--配置驱动-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>

</environment>
</environments>
</configuration>
\n

到此mybatis开发环境已经搭建好,接下来进行开发

\n

Mybatis开发方式

    \n
  1. 原生接口方式
  2. \n
  3. mapper代理实现自定义接口方式
  4. \n
\n
\n

先介绍第一种:原生接口方式

\n
\n

mybatis框架需要开发者自定义sql语句,写在Mapper.xml文件

\n

实际中会为每个实体类创建对应mapper.xml来定义sql语句,来管理该实体类对象

\n

(一)创建Mapper文件

\n

因为前面创建的实体类是Account,所以我这里创建AccountMapper.xml文件

\n

\"image-20210826131020613\"

\n

首先也是要导入约束:

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
\n

AccountMapper.xml:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--1. mapper标签,namespace是当前mapper文件路径-->
<mapper namespace="com.ajream.mapper.AccountMapper">
<!-- 2. 进行增删改查操作,id是后面要调用sql语句时用的的方法名,parameterType是方法中的参数的类型-->
<!-- #{id}等这些都是Account实体类中的属性,表示从中取出值-->
<insert id="save" parameterType="com.ajream.entity.Account">
insert into t_acount(id, username, passwd, age) values (#{id},#{username}, #{passwd}, #{age})
</insert>
</mapper>
\n

说明:

\n
    \n
  1. namespace通常设置为文件所在包名+文件名(无后缀)

    \n
  2. \n
  3. 可以把这个Mapper文件理解成一个java类,类名是 AccountMapper, 有一个方法 save ,可以传入参数,参数类型是 com.ajream.entity.Account

    \n

    这个方法的作用是执行insert插入数据操作

    \n
  4. \n
\n

(二)注册Mapper文件

\n

在mybatis-config.xml配置文件中注册mapper文件, 添加:

\n
1
2
3
<mappers>
\t<mapper resource="com/ajream/mapper/AccountMapper.xml"></mapper>
</mappers>
\n

\"image-20210826133054400\"

\n

编写测试类进行运行

    \n
  1. 根据mybatis配置文件,使用工厂模式生成sqlSession
  2. \n
  3. 执行sql语句
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.ajream.test;

import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class MyTest {
public static void main(String[] args) {
//加载配置文件,获取sqlSession工厂对象
InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis02-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 通过工厂对象获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

String statement= "com.ajream.mapper.AccountMapper.save"; //用于加载mapper配置文件的save方法
Account account = new Account(2,"XiaoZhang", "56789xz", 20);
sqlSession.insert(statement, account); //执行sql语句
sqlSession.commit(); //增、删、改 最后必须提交事务

}
}

\n

注意,如果用ideaIDE,由于xml配置文件默认是在resources中的,其他文件夹的无法识别,因此最后要在pom中添加如下语句,让idea能够识别其他文件夹的xml文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

如果报错:

\n
1
Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效。
\n

在pom.xml中添加语句:

\n
1
2
3
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
\n

\"image-20210826150549895\"

\n
\n

第二种:Mapper代理实现自定义接口方式(推荐)

\n

项目地址mybatis-03

\n
\n

(一)创建接口:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.dao;

import com.ajream.entity.Account;

import java.util.List;

//增删改查
public interface AccountDao {
int save(Account account);
int update(Account account);
int deleteById(int id);
List<Account> findAll();
Account findById(int id);
}

\n

(二)创建接口对应的Mapper.xml文件(通常与接口文件放在同一个文件夹内),通过insert/update/delete/select标签来定义接口方法对应的sql语句

\n

规则:

\n
    \n
  • namespace为接口的全类名

    \n
  • \n
  • id为接口中对应的方法名

    \n
  • \n
  • parameterType为接口中对应方法的参数类型

    \n
  • \n
  • resultType为接口对应方法的返回类型

    \n

    注意:1-如果方法返回的是集合,则resultType是里面元素的类型

    \n

    ​ 2-添加、修改、删除的返回类型默认都是int类型(表示影响的行数),所以不用指定

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ajream.dao.AccountDao">
<insert id="save" parameterType="com.ajream.entity.Account">
insert into t_acount(id, username, passwd, age) values(#{id},#{username}, #{passwd}, #{age})
</insert>
<update id="update" parameterType="com.ajream.entity.Account">
update t_acount set username=#{username},age=#{age},passwd=#{password} where id = #{id}
</update>
<delete id="deleteById" parameterType="int">
delete from t_acount where id = #{id}
</delete>

<select id="findAll" resultType="com.ajream.entity.Account">
select * from t_acount;
</select>

<select id="findById" resultType="com.ajream.entity.Account" parameterType="int">
select * from t_acount where id=#{id}
</select>
</mapper>


\n

(三)注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/AccountDao.xml" />
</mappers>
\n

(四)测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.ajream.test;

import com.ajream.dao.AccountDao;
import com.ajream.entity.Account;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;
import java.util.List;

public class MyTest {
public static void main(String[] args) {
InputStream inputStream = MyTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

// 实现接口的代理对象
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

// 增
// accountDao.save(new Account(8, "小叮当", "12345xdj", 24));
// sqlSession.commit(); //提交事务
// 删
// accountDao.deleteById(4);
// sqlSession.commit(); //提交事务
// 查
// List<Account> list = accountDao.findAll();
// for(Account account: list){
// System.out.println(account);
// }
// Account account = accountDao.findById(2);
// System.out.println(account);

// 改(先查出要修改的对象,再修改对象的各个属性)
Account account1 = accountDao.findById(1);
account1.setUsername("小东");
account1.setAge(19);
account1.setPasswd("56789xd");

int result = accountDao.update(account1);
sqlSession.commit(); //提交事务
System.out.println("修改行数:" + result + "修改后的对象:");
System.out.println(accountDao.findById(1));

sqlSession.close(); //退出会话

}
}

\n

这是进行测试的数据表

\n

\"image-20210826203758449\"

\n"},{"title":"mybatis逆向工程(五)","description":"mybatis框架需要:实体类、自定义mapper接口、mapper.xml,传统开发需要手动创建,而mybatis可以用逆向工程MBG来自动帮助我们创建这3个组件,提高开发效率","abbrlink":"81ecdf4a","date":"2021-08-27T02:22:46.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n\n\n\n\nmybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml\n\n传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率\n\n\n\n\n\n如何使用?\n\n\n\n\n\n(1) 需要额外添加依赖\n\n```xml\n\n org.mybatis.generator\n mybatis-generator-maven-plugin\n 1.3.2\n\n```\n\n\n\n\n\n(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)\n\n\n\n需要配置的信息:\n\n1. jdbcConnection:数据库连接信息\n2. JavaModelGenerator:javaBean(实体类)生成策略\n3. sqlMapGenerator:sql映射文件生成策略\n4. javaClientGenerator:配置Mapper接口生成策略\n5. table :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名)\n\n\n\n首先添加约束\n\n```xml\n\n\n```\n\nGeneratorConfig.xml\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n(3)创建Generator执行类\n\n\n\n```java\npackage com.ajream.test;\n\nimport org.mybatis.generator.api.MyBatisGenerator;\nimport org.mybatis.generator.config.Configuration;\nimport org.mybatis.generator.config.xml.ConfigurationParser;\nimport org.mybatis.generator.exception.InvalidConfigurationException;\nimport org.mybatis.generator.exception.XMLParserException;\nimport org.mybatis.generator.internal.DefaultShellCallback;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n public static void main(String[] args) {\n List warings = new ArrayList();\n boolean overwrite = true;\n String genCig = \"/GeneratorConfig.xml\";\n File configFile = new File(Main.class.getResource(genCig).getFile());\n// File configFile = new File(\"GeneratorConfig.xml\");\n ConfigurationParser configurationParser=new ConfigurationParser(warings);\n Configuration configuration = null;\n try{\n configuration = configurationParser.parseConfiguration(configFile);\n } catch (XMLParserException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n }\n DefaultShellCallback callback = new DefaultShellCallback(overwrite);\n MyBatisGenerator myBatisGenerator = null;\n try{\n myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);\n } catch (InvalidConfigurationException e) {\n e.printStackTrace();\n }\n\n try{\n myBatisGenerator.generate(null);\n } catch (InterruptedException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n } catch (SQLException throwables) {\n throwables.printStackTrace();\n }\n\n System.out.println(\"ok...\");\n }\n}\n\n```\n\n\n\n运行即可在指定路径下生成相应组件\n","source":"_posts/Mybatis/mybatis逆向工程(五).md","raw":"---\ntitle: mybatis逆向工程(五)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: >-\n mybatis框架需要:实体类、自定义mapper接口、mapper.xml,传统开发需要手动创建,而mybatis可以用逆向工程MBG来自动帮助我们创建这3个组件,提高开发效率\nabbrlink: 81ecdf4a\ndate: 2021-08-27 10:22:46\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n\n\n\n\nmybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml\n\n传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率\n\n\n\n\n\n如何使用?\n\n\n\n\n\n(1) 需要额外添加依赖\n\n```xml\n\n org.mybatis.generator\n mybatis-generator-maven-plugin\n 1.3.2\n\n```\n\n\n\n\n\n(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)\n\n\n\n需要配置的信息:\n\n1. jdbcConnection:数据库连接信息\n2. JavaModelGenerator:javaBean(实体类)生成策略\n3. sqlMapGenerator:sql映射文件生成策略\n4. javaClientGenerator:配置Mapper接口生成策略\n5. table :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名)\n\n\n\n首先添加约束\n\n```xml\n\n\n```\n\nGeneratorConfig.xml\n\n```xml\n\n\n\n\n \n \n \n \n \n \n
\n \n\n```\n\n\n\n(3)创建Generator执行类\n\n\n\n```java\npackage com.ajream.test;\n\nimport org.mybatis.generator.api.MyBatisGenerator;\nimport org.mybatis.generator.config.Configuration;\nimport org.mybatis.generator.config.xml.ConfigurationParser;\nimport org.mybatis.generator.exception.InvalidConfigurationException;\nimport org.mybatis.generator.exception.XMLParserException;\nimport org.mybatis.generator.internal.DefaultShellCallback;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n public static void main(String[] args) {\n List warings = new ArrayList();\n boolean overwrite = true;\n String genCig = \"/GeneratorConfig.xml\";\n File configFile = new File(Main.class.getResource(genCig).getFile());\n// File configFile = new File(\"GeneratorConfig.xml\");\n ConfigurationParser configurationParser=new ConfigurationParser(warings);\n Configuration configuration = null;\n try{\n configuration = configurationParser.parseConfiguration(configFile);\n } catch (XMLParserException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n }\n DefaultShellCallback callback = new DefaultShellCallback(overwrite);\n MyBatisGenerator myBatisGenerator = null;\n try{\n myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);\n } catch (InvalidConfigurationException e) {\n e.printStackTrace();\n }\n\n try{\n myBatisGenerator.generate(null);\n } catch (InterruptedException e) {\n e.printStackTrace();\n } catch (IOException e) {\n e.printStackTrace();\n } catch (SQLException throwables) {\n throwables.printStackTrace();\n }\n\n System.out.println(\"ok...\");\n }\n}\n\n```\n\n\n\n运行即可在指定路径下生成相应组件\n","slug":"Mybatis/mybatis逆向工程(五)","published":1,"updated":"2021-08-29T14:45:15.143Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pq003pakveexb0ak5a","content":"

mybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml

\n

传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率

\n

如何使用?

\n

(1) 需要额外添加依赖

\n
1
2
3
4
5
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
</dependency>
\n

(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)

\n

需要配置的信息:

\n
    \n
  1. jdbcConnection:数据库连接信息
  2. \n
  3. JavaModelGenerator:javaBean(实体类)生成策略
  4. \n
  5. sqlMapGenerator:sql映射文件生成策略
  6. \n
  7. javaClientGenerator:配置Mapper接口生成策略
  8. \n
  9. table :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名)
  10. \n
\n

首先添加约束

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
\n

GeneratorConfig.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3"> <!--运行环境必须是MyBatis3-->
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"
userId="root"
password="admin"
/>
<!--targetPackage表示实体类存放在哪个包,targetProject表示包com.ajream.entity存放的路径-->
<javaModelGenerator targetPackage="com.ajream.entity" targetProject="./src/main/java"/>
<sqlMapGenerator targetPackage="com.ajream.dao" targetProject="./src/main/java"/>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.ajream.entity" targetProject="./src/main/java"/>
<table tableName="t_acount" domainObjectName="Account"/>
</context>
</generatorConfiguration>
\n

(3)创建Generator执行类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.ajream.test;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class Main {
public static void main(String[] args) {
List<String> warings = new ArrayList<String>();
boolean overwrite = true;
String genCig = "/GeneratorConfig.xml";
File configFile = new File(Main.class.getResource(genCig).getFile());
// File configFile = new File("GeneratorConfig.xml");
ConfigurationParser configurationParser=new ConfigurationParser(warings);
Configuration configuration = null;
try{
configuration = configurationParser.parseConfiguration(configFile);
} catch (XMLParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = null;
try{
myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);
} catch (InvalidConfigurationException e) {
e.printStackTrace();
}

try{
myBatisGenerator.generate(null);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}

System.out.println("ok...");
}
}

\n

运行即可在指定路径下生成相应组件

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

mybatis框架需要3个组件:实体类、自定义mapper接口、mapper.xml

\n

传统开发需要手动创建,而mybatis可以用逆向工程MBG(MyBatis Generator)来自动帮助我们创建这3个组件,支持基本CRUD操作(复杂的需要自己写), 提高开发效率

\n

如何使用?

\n

(1) 需要额外添加依赖

\n
1
2
3
4
5
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
</dependency>
\n

(2)在resources下创建MBG配置文件GeneratorConfig.xml (文件名可自定义)

\n

需要配置的信息:

\n
    \n
  1. jdbcConnection:数据库连接信息
  2. \n
  3. JavaModelGenerator:javaBean(实体类)生成策略
  4. \n
  5. sqlMapGenerator:sql映射文件生成策略
  6. \n
  7. javaClientGenerator:配置Mapper接口生成策略
  8. \n
  9. table :配置目标数据表(tableName: 表名,domainObjectName:javaBean[实体类]的类名)
  10. \n
\n

首先添加约束

\n
1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
\n

GeneratorConfig.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3"> <!--运行环境必须是MyBatis3-->
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"
userId="root"
password="admin"
/>
<!--targetPackage表示实体类存放在哪个包,targetProject表示包com.ajream.entity存放的路径-->
<javaModelGenerator targetPackage="com.ajream.entity" targetProject="./src/main/java"/>
<sqlMapGenerator targetPackage="com.ajream.dao" targetProject="./src/main/java"/>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.ajream.entity" targetProject="./src/main/java"/>
<table tableName="t_acount" domainObjectName="Account"/>
</context>
</generatorConfiguration>
\n

(3)创建Generator执行类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.ajream.test;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class Main {
public static void main(String[] args) {
List<String> warings = new ArrayList<String>();
boolean overwrite = true;
String genCig = "/GeneratorConfig.xml";
File configFile = new File(Main.class.getResource(genCig).getFile());
// File configFile = new File("GeneratorConfig.xml");
ConfigurationParser configurationParser=new ConfigurationParser(warings);
Configuration configuration = null;
try{
configuration = configurationParser.parseConfiguration(configFile);
} catch (XMLParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = null;
try{
myBatisGenerator = new MyBatisGenerator(configuration,callback,warings);
} catch (InvalidConfigurationException e) {
e.printStackTrace();
}

try{
myBatisGenerator.generate(null);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}

System.out.println("ok...");
}
}

\n

运行即可在指定路径下生成相应组件

\n"},{"title":"esp8266(1)-接入点模式-无线终端模式","abbrlink":"4dc0a7a8","date":"2021-08-02T00:00:00.000Z","description":"esp8266如何开启热点(接入点模式)、如何连接WiFi(无线终端模式)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n\n接入点模式:开发板作为路由器(网关),可以让其他设备连接\n\n无线终端模式:开发板作为终端,可以连接路由器WiFi\n\n\n\n> 说明:此处使用ArduinoIDE来进行开发。\n>\n> 注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了\n\n\n\n![ESP8266-NodeMCU引脚功能](http://www.taichi-maker.com/wp-content/uploads/2019/02/esp8266_devkit_horizontal-001.png)\n\n\n\n## 1、配置接入点模式\n\n即开启 “热点”\n\n1. 导入 `ESP8266WiFi.h`库\n\n2. 配置WiFi名称(ssid)、密码(passwd)—— `softAP()`\n\n ```c\n const char ssid[] = \"MyWiFi\";\n const char passwd[] = \"12345678\";\n WiFi.softAP(ssid, passwd);\n ```\n\n3. 获取开发板IP\n\n ```c\n WiFi.softAPIP();\n ```\n\n\n\n完整code\n\n```c\n#include \n\nchar ssid[] = \"MyWiFi\";\nchar passwd[] = \"1234567890\";\n\nvoid setup(){\n Serial.begin(9600);\n \n Serial.print(\"wifi ssid: \");\n Serial.println(ssid);\n Serial.print(\"wifi password: \");\n Serial.println(passwd);\n \n WiFi.softAP(ssid, passwd);\t//开启接入点模式\n\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.softAPIP());\t//打印开发板的IP\n}\n\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n## 2、配置无线终端模式\n\n### 只有1个WiFi\n\n将开发板连接WiFi\n\n```c\n//ssid、passwd均为字符串\nWiFi.mode(WIFI_STA);\nWiFi.begin(ssid, passwd);\n```\n\n\n\n多次尝试连接,直到连接上:\n\n```c\nwhile(WiFi.status() != WL_CONNECTED\t){\n delay(1000);\n //....\n}\n```\n\n\n\n连上WiFi后,获取wifi的ssid、开发板IP地址:\n\n```c\nWiFi.SSID();\t\t//返回字符串\nWiFi.localIP(); //返回字符串\n```\n\n\n\n完整code:\n\n```c\n#include // 本程序使用ESP8266WiFi库\n\nString ssid = \"aaa\"; \nString password = \"12345678\"; \n \n \nvoid setup() {\n Serial.begin(9600); \n WiFi.begin(ssid, password); \n \n int i = 0; \n while (WiFi.status() != WL_CONNECTED) { \n delay(1000); \n Serial.print(i++); Serial.print(' '); \n } \n \n Serial.println(\"\"); \n Serial.print(\"Connecting to \"); \n Serial.println(ssid); \n \n Serial.println(\"IP address: \" ); \n Serial.println(WiFi.localIP()); \n}\n\nvoid loop() { \n}\n```\n\n\n\n### 多个WiFi,自动连接最强信号的\n\n1. 先添加几个待选WiFi\n\n ```c\n //导入库\n #include \n \n // 创建ESP8266WiFiMulti对象\n ESP8266WiFiMulti wifiMulti; \n \n //通过addAP()函数存储WiFi的ssid和密码\n wifiMulti.addAP(\"aaaa\", \"11111111\");\n wifiMulti.addAP(\"bbbb\", \"22222222\");\n wifiMulti.addAP(\"cccc\", \"33333333\");\n \n ```\n\n \n\n2. 多次尝试连接:`wifiMulti.run()`\n\n ```c\n // 将会连接信号最强的那一个WiFi信号。\n while (wifiMulti.run() != WL_CONNECTED) { \n delay(1000); \n Serial.print('...'); \n } \n ```\n\n \n\n\n\n完整code:\n\n```c\n#include \n#include \n\nESP8266WiFiMulti wifiMulti; //创建对象\n\nvoid setup(){\n Serial.begin(9600);\n wifiMulti.addAP(\"aaa\", \"12345678\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(1000);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\nvoid loop(){\n\n}\n```\n\n","source":"_posts/esp8266/1-接入点模式-无线终端模式.md","raw":"---\ntitle: esp8266(1)-接入点模式-无线终端模式\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 4dc0a7a8\ndate: 2021-08-02 08:00:00\ndescription: esp8266如何开启热点(接入点模式)、如何连接WiFi(无线终端模式)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n\n接入点模式:开发板作为路由器(网关),可以让其他设备连接\n\n无线终端模式:开发板作为终端,可以连接路由器WiFi\n\n\n\n> 说明:此处使用ArduinoIDE来进行开发。\n>\n> 注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了\n\n\n\n![ESP8266-NodeMCU引脚功能](http://www.taichi-maker.com/wp-content/uploads/2019/02/esp8266_devkit_horizontal-001.png)\n\n\n\n## 1、配置接入点模式\n\n即开启 “热点”\n\n1. 导入 `ESP8266WiFi.h`库\n\n2. 配置WiFi名称(ssid)、密码(passwd)—— `softAP()`\n\n ```c\n const char ssid[] = \"MyWiFi\";\n const char passwd[] = \"12345678\";\n WiFi.softAP(ssid, passwd);\n ```\n\n3. 获取开发板IP\n\n ```c\n WiFi.softAPIP();\n ```\n\n\n\n完整code\n\n```c\n#include \n\nchar ssid[] = \"MyWiFi\";\nchar passwd[] = \"1234567890\";\n\nvoid setup(){\n Serial.begin(9600);\n \n Serial.print(\"wifi ssid: \");\n Serial.println(ssid);\n Serial.print(\"wifi password: \");\n Serial.println(passwd);\n \n WiFi.softAP(ssid, passwd);\t//开启接入点模式\n\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.softAPIP());\t//打印开发板的IP\n}\n\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n## 2、配置无线终端模式\n\n### 只有1个WiFi\n\n将开发板连接WiFi\n\n```c\n//ssid、passwd均为字符串\nWiFi.mode(WIFI_STA);\nWiFi.begin(ssid, passwd);\n```\n\n\n\n多次尝试连接,直到连接上:\n\n```c\nwhile(WiFi.status() != WL_CONNECTED\t){\n delay(1000);\n //....\n}\n```\n\n\n\n连上WiFi后,获取wifi的ssid、开发板IP地址:\n\n```c\nWiFi.SSID();\t\t//返回字符串\nWiFi.localIP(); //返回字符串\n```\n\n\n\n完整code:\n\n```c\n#include // 本程序使用ESP8266WiFi库\n\nString ssid = \"aaa\"; \nString password = \"12345678\"; \n \n \nvoid setup() {\n Serial.begin(9600); \n WiFi.begin(ssid, password); \n \n int i = 0; \n while (WiFi.status() != WL_CONNECTED) { \n delay(1000); \n Serial.print(i++); Serial.print(' '); \n } \n \n Serial.println(\"\"); \n Serial.print(\"Connecting to \"); \n Serial.println(ssid); \n \n Serial.println(\"IP address: \" ); \n Serial.println(WiFi.localIP()); \n}\n\nvoid loop() { \n}\n```\n\n\n\n### 多个WiFi,自动连接最强信号的\n\n1. 先添加几个待选WiFi\n\n ```c\n //导入库\n #include \n \n // 创建ESP8266WiFiMulti对象\n ESP8266WiFiMulti wifiMulti; \n \n //通过addAP()函数存储WiFi的ssid和密码\n wifiMulti.addAP(\"aaaa\", \"11111111\");\n wifiMulti.addAP(\"bbbb\", \"22222222\");\n wifiMulti.addAP(\"cccc\", \"33333333\");\n \n ```\n\n \n\n2. 多次尝试连接:`wifiMulti.run()`\n\n ```c\n // 将会连接信号最强的那一个WiFi信号。\n while (wifiMulti.run() != WL_CONNECTED) { \n delay(1000); \n Serial.print('...'); \n } \n ```\n\n \n\n\n\n完整code:\n\n```c\n#include \n#include \n\nESP8266WiFiMulti wifiMulti; //创建对象\n\nvoid setup(){\n Serial.begin(9600);\n wifiMulti.addAP(\"aaa\", \"12345678\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(1000);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\nvoid loop(){\n\n}\n```\n\n","slug":"esp8266/1-接入点模式-无线终端模式","published":1,"updated":"2021-09-10T03:19:46.998Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pr003sakve2exe8imp","content":"

接入点模式:开发板作为路由器(网关),可以让其他设备连接

\n

无线终端模式:开发板作为终端,可以连接路由器WiFi

\n
\n

说明:此处使用ArduinoIDE来进行开发。

\n

注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了

\n
\n

\"ESP8266-NodeMCU引脚功能\"

\n

1、配置接入点模式

即开启 “热点”

\n
    \n
  1. 导入 ESP8266WiFi.h

    \n
  2. \n
  3. 配置WiFi名称(ssid)、密码(passwd)—— softAP()

    \n
    1
    2
    3
    const char ssid[] = "MyWiFi";
    const char passwd[] = "12345678";
    WiFi.softAP(ssid, passwd);
    \n
  4. \n
  5. 获取开发板IP

    \n
    1
    WiFi.softAPIP();
    \n
  6. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <ESP8266WiFi.h>

char ssid[] = "MyWiFi";
char passwd[] = "1234567890";

void setup(){
Serial.begin(9600);

Serial.print("wifi ssid: ");
Serial.println(ssid);
Serial.print("wifi password: ");
Serial.println(passwd);

WiFi.softAP(ssid, passwd);\t//开启接入点模式

Serial.print("IP Address: ");
Serial.println(WiFi.softAPIP());\t//打印开发板的IP
}


void loop(){

}
\n

2、配置无线终端模式

只有1个WiFi

将开发板连接WiFi

\n
1
2
3
//ssid、passwd均为字符串
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passwd);
\n

多次尝试连接,直到连接上:

\n
1
2
3
4
while(WiFi.status() != WL_CONNECTED\t){
delay(1000);
//....
}
\n

连上WiFi后,获取wifi的ssid、开发板IP地址:

\n
1
2
WiFi.SSID();\t\t//返回字符串
WiFi.localIP(); //返回字符串
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <ESP8266WiFi.h>        // 本程序使用ESP8266WiFi库

String ssid = "aaa";
String password = "12345678";


void setup() {
Serial.begin(9600);
WiFi.begin(ssid, password);

int i = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(i++); Serial.print(' ');
}

Serial.println("");
Serial.print("Connecting to ");
Serial.println(ssid);

Serial.println("IP address: " );
Serial.println(WiFi.localIP());
}

void loop() {
}
\n

多个WiFi,自动连接最强信号的

    \n
  1. 先添加几个待选WiFi

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //导入库
    #include <ESP8266WiFiMulti.h>

    // 创建ESP8266WiFiMulti对象
    ESP8266WiFiMulti wifiMulti;

    //通过addAP()函数存储WiFi的ssid和密码
    wifiMulti.addAP("aaaa", "11111111");
    wifiMulti.addAP("bbbb", "22222222");
    wifiMulti.addAP("cccc", "33333333");

    \n
  2. \n
\n
    \n
  1. 多次尝试连接:wifiMulti.run()

    \n
    1
    2
    3
    4
    5
     // 将会连接信号最强的那一个WiFi信号。
    while (wifiMulti.run() != WL_CONNECTED) {
    delay(1000);
    Serial.print('...');
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

ESP8266WiFiMulti wifiMulti; //创建对象

void setup(){
Serial.begin(9600);
wifiMulti.addAP("aaa", "12345678");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(1000);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());

}

void loop(){

}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

接入点模式:开发板作为路由器(网关),可以让其他设备连接

\n

无线终端模式:开发板作为终端,可以连接路由器WiFi

\n
\n

说明:此处使用ArduinoIDE来进行开发。

\n

注意:上传代码前必须把“串口监视器” 关闭,否则可能上传出错,因为端口被占用了

\n
\n

\"ESP8266-NodeMCU引脚功能\"

\n

1、配置接入点模式

即开启 “热点”

\n
    \n
  1. 导入 ESP8266WiFi.h

    \n
  2. \n
  3. 配置WiFi名称(ssid)、密码(passwd)—— softAP()

    \n
    1
    2
    3
    const char ssid[] = "MyWiFi";
    const char passwd[] = "12345678";
    WiFi.softAP(ssid, passwd);
    \n
  4. \n
  5. 获取开发板IP

    \n
    1
    WiFi.softAPIP();
    \n
  6. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <ESP8266WiFi.h>

char ssid[] = "MyWiFi";
char passwd[] = "1234567890";

void setup(){
Serial.begin(9600);

Serial.print("wifi ssid: ");
Serial.println(ssid);
Serial.print("wifi password: ");
Serial.println(passwd);

WiFi.softAP(ssid, passwd);\t//开启接入点模式

Serial.print("IP Address: ");
Serial.println(WiFi.softAPIP());\t//打印开发板的IP
}


void loop(){

}
\n

2、配置无线终端模式

只有1个WiFi

将开发板连接WiFi

\n
1
2
3
//ssid、passwd均为字符串
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, passwd);
\n

多次尝试连接,直到连接上:

\n
1
2
3
4
while(WiFi.status() != WL_CONNECTED\t){
delay(1000);
//....
}
\n

连上WiFi后,获取wifi的ssid、开发板IP地址:

\n
1
2
WiFi.SSID();\t\t//返回字符串
WiFi.localIP(); //返回字符串
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <ESP8266WiFi.h>        // 本程序使用ESP8266WiFi库

String ssid = "aaa";
String password = "12345678";


void setup() {
Serial.begin(9600);
WiFi.begin(ssid, password);

int i = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(i++); Serial.print(' ');
}

Serial.println("");
Serial.print("Connecting to ");
Serial.println(ssid);

Serial.println("IP address: " );
Serial.println(WiFi.localIP());
}

void loop() {
}
\n

多个WiFi,自动连接最强信号的

    \n
  1. 先添加几个待选WiFi

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //导入库
    #include <ESP8266WiFiMulti.h>

    // 创建ESP8266WiFiMulti对象
    ESP8266WiFiMulti wifiMulti;

    //通过addAP()函数存储WiFi的ssid和密码
    wifiMulti.addAP("aaaa", "11111111");
    wifiMulti.addAP("bbbb", "22222222");
    wifiMulti.addAP("cccc", "33333333");

    \n
  2. \n
\n
    \n
  1. 多次尝试连接:wifiMulti.run()

    \n
    1
    2
    3
    4
    5
     // 将会连接信号最强的那一个WiFi信号。
    while (wifiMulti.run() != WL_CONNECTED) {
    delay(1000);
    Serial.print('...');
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

ESP8266WiFiMulti wifiMulti; //创建对象

void setup(){
Serial.begin(9600);
wifiMulti.addAP("aaa", "12345678");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(1000);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());

}

void loop(){

}
\n"},{"title":"esp8266(0)-Arduino配置esp8266开发环境","description":"如何用Arduino配置esp8266开发环境","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","abbrlink":"89cf4bca","date":"2021-08-01T00:10:21.000Z","_content":"\n\n\n\n\n### 一般方法(要求网速较好)\n\n\n\n1. 打开ArduinoIDE,在 **文件->首选项** 的附加开发板管理器网址 添加下面地址:\n\n ```http\n http://arduino.esp8266.com/stable/package_esp8266com_index.json\n ```\n\n ![image-20210807195949825](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-16-02_image-20210807195949825.png)\n\n2. 在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可\n\n ![image-20210807201352339](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-13-53_image-20210807201352339.png)\n\n\n\n\n\n### 特殊方法(适用于网络不好)\n\n一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可\n\n**过程较为麻烦,不想看的直接看最后:**\n\n1. 去[earlephilhower](https://github.com/earlephilhower/esp-quick-toolchain/releases/)下载下面这4个zip文件:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip\n python3-3.7.2.post1-embed-win32v2a.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip\n ```\n\n2. 去[esp8266-Arduino](https://github.com/esp8266/Arduino/releases/tag/3.0.2)下载esp8266-3.0.2.zip\n\n ![image-20210807202721141](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-27-22_image-20210807202721141.png)\n\n3. 克隆这个仓库,或者下载zip包\n\n ```git\n https://github.com/esp8266/Arduino.git\n ```\n\n 解压后改名为esp8266,放到目录(没有的文件夹自己新建):\n\n ```\n C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n ```\n\n4. 用第2步下载的zip包中的 `libraries` 文件夹来代替第3步下载的文件夹中 的`libraries`\n\n5. 将第一步下载的4个zip包分别重命名:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip --> xtensa-lx106-elf.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip\n python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip\n ```\n\n 然后解压到在第三步的 `tool`文件夹中,下面是解压后:\n\n ![image-20210807203943403](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-39-44_image-20210807203943403.png)\n\n\n\n\n\n> 我已经把这5步全部做完,放到压缩包 `esp8266.zip` 中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:\n>\n> ```\n> C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n> ```\n\n\n\n","source":"_posts/esp8266/0-Arduino配置esp8266开发环境.md","raw":"---\ntitle: esp8266(0)-Arduino配置esp8266开发环境\ntags:\n - esp8266\ncategories:\n - 硬件学习\n - esp8266\ndescription: 如何用Arduino配置esp8266开发环境\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png'\nabbrlink: 89cf4bca\ndate: 2021-08-01 08:10:21\n---\n\n\n\n\n\n### 一般方法(要求网速较好)\n\n\n\n1. 打开ArduinoIDE,在 **文件->首选项** 的附加开发板管理器网址 添加下面地址:\n\n ```http\n http://arduino.esp8266.com/stable/package_esp8266com_index.json\n ```\n\n ![image-20210807195949825](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-16-02_image-20210807195949825.png)\n\n2. 在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可\n\n ![image-20210807201352339](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-13-53_image-20210807201352339.png)\n\n\n\n\n\n### 特殊方法(适用于网络不好)\n\n一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可\n\n**过程较为麻烦,不想看的直接看最后:**\n\n1. 去[earlephilhower](https://github.com/earlephilhower/esp-quick-toolchain/releases/)下载下面这4个zip文件:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip\n python3-3.7.2.post1-embed-win32v2a.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip\n ```\n\n2. 去[esp8266-Arduino](https://github.com/esp8266/Arduino/releases/tag/3.0.2)下载esp8266-3.0.2.zip\n\n ![image-20210807202721141](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-27-22_image-20210807202721141.png)\n\n3. 克隆这个仓库,或者下载zip包\n\n ```git\n https://github.com/esp8266/Arduino.git\n ```\n\n 解压后改名为esp8266,放到目录(没有的文件夹自己新建):\n\n ```\n C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n ```\n\n4. 用第2步下载的zip包中的 `libraries` 文件夹来代替第3步下载的文件夹中 的`libraries`\n\n5. 将第一步下载的4个zip包分别重命名:\n\n ```\n x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip --> xtensa-lx106-elf.zip\n x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip\n python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip\n x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip\n ```\n\n 然后解压到在第三步的 `tool`文件夹中,下面是解压后:\n\n ![image-20210807203943403](https://gitee.com/ajream/images/raw/master/img/2021-08-0720-39-44_image-20210807203943403.png)\n\n\n\n\n\n> 我已经把这5步全部做完,放到压缩包 `esp8266.zip` 中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:\n>\n> ```\n> C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\\n> ```\n\n\n\n","slug":"esp8266/0-Arduino配置esp8266开发环境","published":1,"updated":"2021-09-11T07:55:32.130Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ps003uakve66vo01gc","content":"\n

一般方法(要求网速较好)

    \n
  1. 打开ArduinoIDE,在 文件->首选项附加开发板管理器网址 添加下面地址:

    \n
    1
    http://arduino.esp8266.com/stable/package_esp8266com_index.json
    \n

    \"image-20210807195949825\"

    \n
  2. \n
  3. 在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可

    \n

    \"image-20210807201352339\"

    \n
  4. \n
\n

特殊方法(适用于网络不好)

一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可

\n

过程较为麻烦,不想看的直接看最后:

\n
    \n
  1. earlephilhower下载下面这4个zip文件:

    \n
    1
    2
    3
    4
    x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip
    x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip
    python3-3.7.2.post1-embed-win32v2a.zip
    x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip
    \n
  2. \n
  3. esp8266-Arduino下载esp8266-3.0.2.zip

    \n

    \"image-20210807202721141\"

    \n
  4. \n
  5. 克隆这个仓库,或者下载zip包

    \n
    1
    https://github.com/esp8266/Arduino.git
    \n

    解压后改名为esp8266,放到目录(没有的文件夹自己新建):

    \n
    1
    C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
    \n
  6. \n
  7. 用第2步下载的zip包中的 libraries 文件夹来代替第3步下载的文件夹中 的libraries

    \n
  8. \n
  9. 将第一步下载的4个zip包分别重命名:

    \n
    1
    2
    3
    4
    x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip  -->  xtensa-lx106-elf.zip
    x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip
    python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip
    x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip
    \n

    然后解压到在第三步的 tool文件夹中,下面是解压后:

    \n

    \"image-20210807203943403\"

    \n
  10. \n
\n
\n

我已经把这5步全部做完,放到压缩包 esp8266.zip 中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:

\n
1
C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"\n

一般方法(要求网速较好)

    \n
  1. 打开ArduinoIDE,在 文件->首选项附加开发板管理器网址 添加下面地址:

    \n
    1
    http://arduino.esp8266.com/stable/package_esp8266com_index.json
    \n

    \"image-20210807195949825\"

    \n
  2. \n
  3. 在工具->开发板->开发板管理器,搜索 esp8266,然后选择安装即可

    \n

    \"image-20210807201352339\"

    \n
  4. \n
\n

特殊方法(适用于网络不好)

一般方法中如果因为网速过慢,可能在下载过程中会比较慢甚至出错,因此可以自己去GitHub下载对应文件再放到指定文件夹下即可

\n

过程较为麻烦,不想看的直接看最后:

\n
    \n
  1. earlephilhower下载下面这4个zip文件:

    \n
    1
    2
    3
    4
    x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip
    x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip
    python3-3.7.2.post1-embed-win32v2a.zip
    x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip
    \n
  2. \n
  3. esp8266-Arduino下载esp8266-3.0.2.zip

    \n

    \"image-20210807202721141\"

    \n
  4. \n
  5. 克隆这个仓库,或者下载zip包

    \n
    1
    https://github.com/esp8266/Arduino.git
    \n

    解压后改名为esp8266,放到目录(没有的文件夹自己新建):

    \n
    1
    C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
    \n
  6. \n
  7. 用第2步下载的zip包中的 libraries 文件夹来代替第3步下载的文件夹中 的libraries

    \n
  8. \n
  9. 将第一步下载的4个zip包分别重命名:

    \n
    1
    2
    3
    4
    x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip  -->  xtensa-lx106-elf.zip
    x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip --> mkspiffs.zip
    python3-3.7.2.post1-embed-win32v2a.zip --> python3.zip
    x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip --> mklittlefs.zip
    \n

    然后解压到在第三步的 tool文件夹中,下面是解压后:

    \n

    \"image-20210807203943403\"

    \n
  10. \n
\n
\n

我已经把这5步全部做完,放到压缩包 esp8266.zip 中,需要的可以去百度网盘下载【链接: https://pan.baidu.com/s/1UqUJXgvzQecXZjqRe_JLaA 提取码: 6xpa】,或者csdn下载【https://download.csdn.net/download/m0_46079750/20887320】,并解压到 下面的目录即可:

\n
1
C:\\Users\\用户名\\Documents\\Arduino\\hardware\\esp8266com\\
\n
\n"},{"title":"esp8266(2)-网络服务器","abbrlink":"27153554","date":"2021-08-02T00:20:00.000Z","description":"esp8266作为服务器使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n## 1、建立基本网络服务器\n\n\n\n1. 导入库文件 `ESP8266WebServer.h`\n\n2. 创建服务器对象\n\n ```c\n ESP8266WebServer server(80); //80是端口号\n ```\n\n3. 启动服务器\n\n ```c\n server.begin();\n ```\n\n4. 服务器访问配置\n\n ```c\n // 访问根节点时,调用处理函数 handleRoot\n server.on(\"/\", handleRoot);\n \n //访问不到,即404时,调用处理函数 handleNotFound\n server.onNotFound(handleNotFound);\n ```\n\n5. 访问处理函数\n\n ```c\n void handleRoot(){\n server.send(200, \"text/plain\", \"Hello from ESP8266\");\n }\n ```\n\n ```c\n void handleNotFound(){\n server.send(404, \"text/plain\", \"404: Not found\");\n }\n ```\n\n \n\n6. 循环监听端口\n\n ```c\n void loop(){\n server.handleClient(); //循环监听客户端访问情况\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n server.begin();\n server.on(\"/\", handleRoot);\n server.onNotFound(handleNotFound);\n\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\n//监听客户端\nvoid loop(){\n server.handleClient();\n}\n\n//访问处理函数\nvoid handleRoot(){\n server.send(200, \"text/plain\", \"Hello esp8266\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n\n\n## 2、通过网络服务器实现开发板控制\n\n通过网页控制nodeMCU开发板小灯亮灭\n\n步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动\n\n3. 服务器访问配置\n\n ```c\n server.on(\"/\", HTTP_GET, handleRoot);\n \n /*添加这个控制LED的网页, 使用post方式发送http请求*/\n server.on(\"/LED\", HTTP_POST, handleLED); \n \n server.onNotFound(handleNotFound);\n ```\n\n4. 访问处理函数\n\n ```c\n \n void handleRoot(){\n server.send(200, \"text/html\", \"
\");\n }\n ```\n\n > 注意这句html代码:\n >\n > ```html\n >
\n > \n >
\n > ```\n >\n > 这会生成一个按钮,点击这个按钮会把数据发到\"/LED\"页面\n\n \n\n 下面是“/LED”页面的处理函数:\n\n ```c\n void handleLED(){\n static bool LEDState = LOW;//记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n }\n ```\n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.on(\"/LED\", HTTP_POST, handleLED);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n}\n\nvoid handleRoot(){\n server.send(200, \"text/html\", \"
\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n\nvoid handleLED(){\n static bool LEDState = LOW; //记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n}\n```\n\n\n\n## 3、将开发板引脚状态发送到终端网页\n\n将引脚**D3**(已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中\n\n1. 首先在开始时将D3引脚设置为上拉输入模式\n\n ```c\n pinMode(D3, INPUT_PULLUP); \n ```\n\n2. 循环读取引脚状态 `digitalRead()`\n\n ```c\n bool pinState;\n void loop(){\n pinState = digitalRead(D3); // 获取引脚状态\n //......\n }\n ```\n\n \n\n3. 要在根节点下显示引脚状态,所以其访问处理函数为:\n\n ```c\n \n void handleRoot() { \n String displayPinState; // 存储按键状态的字符串变量\n \n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n esp8266_server.send(200, \"text/plain\", displayPinState); \n // 向浏览器发送按键状态信息 \n }\n ```\n\n \n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nbool pinState;\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(D3, INPUT_PULLUP); //上拉输入模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(800);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n pinState = digitalRead(D3);\n}\n\nvoid handleRoot(){\n String displayPinState;\n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n server.send(200, \"text/plain\", displayPinState);\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n> 注意:引脚状态改变后需要手动刷新网页,网页显示才会改变\n\n\n\n\n\n","source":"_posts/esp8266/2-网络服务器.md","raw":"---\ntitle: esp8266(2)-网络服务器\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: '27153554'\ndate: 2021-08-02 08:20:00\ndescription: esp8266作为服务器使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n## 1、建立基本网络服务器\n\n\n\n1. 导入库文件 `ESP8266WebServer.h`\n\n2. 创建服务器对象\n\n ```c\n ESP8266WebServer server(80); //80是端口号\n ```\n\n3. 启动服务器\n\n ```c\n server.begin();\n ```\n\n4. 服务器访问配置\n\n ```c\n // 访问根节点时,调用处理函数 handleRoot\n server.on(\"/\", handleRoot);\n \n //访问不到,即404时,调用处理函数 handleNotFound\n server.onNotFound(handleNotFound);\n ```\n\n5. 访问处理函数\n\n ```c\n void handleRoot(){\n server.send(200, \"text/plain\", \"Hello from ESP8266\");\n }\n ```\n\n ```c\n void handleNotFound(){\n server.send(404, \"text/plain\", \"404: Not found\");\n }\n ```\n\n \n\n6. 循环监听端口\n\n ```c\n void loop(){\n server.handleClient(); //循环监听客户端访问情况\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n server.begin();\n server.on(\"/\", handleRoot);\n server.onNotFound(handleNotFound);\n\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n \n}\n\n//监听客户端\nvoid loop(){\n server.handleClient();\n}\n\n//访问处理函数\nvoid handleRoot(){\n server.send(200, \"text/plain\", \"Hello esp8266\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n\n\n## 2、通过网络服务器实现开发板控制\n\n通过网页控制nodeMCU开发板小灯亮灭\n\n步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动\n\n3. 服务器访问配置\n\n ```c\n server.on(\"/\", HTTP_GET, handleRoot);\n \n /*添加这个控制LED的网页, 使用post方式发送http请求*/\n server.on(\"/LED\", HTTP_POST, handleLED); \n \n server.onNotFound(handleNotFound);\n ```\n\n4. 访问处理函数\n\n ```c\n \n void handleRoot(){\n server.send(200, \"text/html\", \"
\");\n }\n ```\n\n > 注意这句html代码:\n >\n > ```html\n >
\n > \n >
\n > ```\n >\n > 这会生成一个按钮,点击这个按钮会把数据发到\"/LED\"页面\n\n \n\n 下面是“/LED”页面的处理函数:\n\n ```c\n void handleLED(){\n static bool LEDState = LOW;//记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n }\n ```\n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.on(\"/LED\", HTTP_POST, handleLED);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(600);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n}\n\nvoid handleRoot(){\n server.send(200, \"text/html\", \"
\");\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n\nvoid handleLED(){\n static bool LEDState = LOW; //记录LED此时亮灭状态\n LEDState = !LEDState;\n digitalWrite(LED_BUILTIN, LEDState);\n server.sendHeader(\"Location\", \"/\");\n server.send(303); //303表示将网页重定向\n}\n```\n\n\n\n## 3、将开发板引脚状态发送到终端网页\n\n将引脚**D3**(已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中\n\n1. 首先在开始时将D3引脚设置为上拉输入模式\n\n ```c\n pinMode(D3, INPUT_PULLUP); \n ```\n\n2. 循环读取引脚状态 `digitalRead()`\n\n ```c\n bool pinState;\n void loop(){\n pinState = digitalRead(D3); // 获取引脚状态\n //......\n }\n ```\n\n \n\n3. 要在根节点下显示引脚状态,所以其访问处理函数为:\n\n ```c\n \n void handleRoot() { \n String displayPinState; // 存储按键状态的字符串变量\n \n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n esp8266_server.send(200, \"text/plain\", displayPinState); \n // 向浏览器发送按键状态信息 \n }\n ```\n\n \n\n\n\n完整code\n\n```c\n#include \n#include \n#include \n\nESP8266WiFiMulti wifiMulti;\nESP8266WebServer server(80); //80是端口号\n\nbool pinState;\n\nvoid setup(){\n Serial.begin(9600);\n\n pinMode(D3, INPUT_PULLUP); //上拉输入模式\n \n /*服务器配置*/\n server.begin();\n server.on(\"/\", HTTP_GET, handleRoot);\n server.onNotFound(handleNotFound);\n\n /*Wifi配置*/\n wifiMulti.addAP(\"RBook\", \"17191719\");\n wifiMulti.addAP(\"bbb\", \"12345678\");\n wifiMulti.addAP(\"ccc\", \"12345678\");\n\n int i = 0;\n while (wifiMulti.run() != WL_CONNECTED){\n delay(800);\n Serial.print(++i);\n Serial.print(\" \");\n }\n\n Serial.println(\"\");\n Serial.print(\"IP Address: \");\n Serial.println(WiFi.localIP());\n}\n\nvoid loop(){\n server.handleClient();\n pinState = digitalRead(D3);\n}\n\nvoid handleRoot(){\n String displayPinState;\n if(pinState == HIGH){ // 当按键引脚D3为高电平\n displayPinState = \"Button State: HIGH\"; // 字符串赋值高电平信息\n } else { // 当按键引脚D3为低电平\n displayPinState = \"Button State: LOW\"; // 字符串赋值低电平信息\n }\n server.send(200, \"text/plain\", displayPinState);\n}\n\nvoid handleNotFound(){\n server.send(404, \"text/plain\", \"404 not found\");\n}\n```\n\n> 注意:引脚状态改变后需要手动刷新网页,网页显示才会改变\n\n\n\n\n\n","slug":"esp8266/2-网络服务器","published":1,"updated":"2021-09-10T03:19:57.136Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pt003xakve53qs5vxp","content":"

1、建立基本网络服务器

    \n
  1. 导入库文件 ESP8266WebServer.h

    \n
  2. \n
  3. 创建服务器对象

    \n
    1
    ESP8266WebServer server(80);  //80是端口号
    \n
  4. \n
  5. 启动服务器

    \n
    1
    server.begin();
    \n
  6. \n
  7. 服务器访问配置

    \n
    1
    2
    3
    4
    5
    // 访问根节点时,调用处理函数 handleRoot
    server.on("/", handleRoot);

    //访问不到,即404时,调用处理函数 handleNotFound
    server.onNotFound(handleNotFound);
    \n
  8. \n
  9. 访问处理函数

    \n
    1
    2
    3
    void handleRoot(){
    server.send(200, "text/plain", "Hello from ESP8266");
    }
    \n
    1
    2
    3
    void handleNotFound(){
    server.send(404, "text/plain", "404: Not found");
    }
    \n
  10. \n
\n
    \n
  1. 循环监听端口

    \n
    1
    2
    3
    void loop(){
    server.handleClient(); //循环监听客户端访问情况
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

void setup(){
Serial.begin(9600);
server.begin();
server.on("/", handleRoot);
server.onNotFound(handleNotFound);

wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(600);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());

}

//监听客户端
void loop(){
server.handleClient();
}

//访问处理函数
void handleRoot(){
server.send(200, "text/plain", "Hello esp8266");
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}
\n

2、通过网络服务器实现开发板控制

通过网页控制nodeMCU开发板小灯亮灭

\n

步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动

\n
    \n
  1. 服务器访问配置

    \n
    1
    2
    3
    4
    5
    6
    server.on("/", HTTP_GET, handleRoot);

    /*添加这个控制LED的网页, 使用post方式发送http请求*/
    server.on("/LED", HTTP_POST, handleLED);

    server.onNotFound(handleNotFound);
    \n
  2. \n
  3. 访问处理函数

    \n
    1
    2
    3
    4

    void handleRoot(){
    server.send(200, "text/html", "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>");
    }
    \n
    \n

    注意这句html代码:

    \n
    1
    2
    3
    <form action="/LED" method="POST">
    <input type="submit" value="Toggle LED">
    </form>
    \n

    这会生成一个按钮,点击这个按钮会把数据发到”/LED”页面

    \n
    \n
  4. \n
\n

下面是“/LED”页面的处理函数:

\n
1
2
3
4
5
6
7
void handleLED(){
static bool LEDState = LOW;//记录LED此时亮灭状态
LEDState = !LEDState;
digitalWrite(LED_BUILTIN, LEDState);
server.sendHeader("Location", "/");
server.send(303); //303表示将网页重定向
}
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

void setup(){
Serial.begin(9600);

pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式

/*服务器配置*/
server.begin();
server.on("/", HTTP_GET, handleRoot);
server.on("/LED", HTTP_POST, handleLED);
server.onNotFound(handleNotFound);

/*Wifi配置*/
wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(600);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}

void loop(){
server.handleClient();
}

void handleRoot(){
server.send(200, "text/html", "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>");
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}

void handleLED(){
static bool LEDState = LOW; //记录LED此时亮灭状态
LEDState = !LEDState;
digitalWrite(LED_BUILTIN, LEDState);
server.sendHeader("Location", "/");
server.send(303); //303表示将网页重定向
}
\n

3、将开发板引脚状态发送到终端网页

将引脚D3(已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中

\n
    \n
  1. 首先在开始时将D3引脚设置为上拉输入模式

    \n
    1
    pinMode(D3, INPUT_PULLUP); 
    \n
  2. \n
  3. 循环读取引脚状态 digitalRead()

    \n
    1
    2
    3
    4
    5
    bool pinState;
    void loop(){
    pinState = digitalRead(D3); // 获取引脚状态
    //......
    }
    \n
  4. \n
\n
    \n
  1. 要在根节点下显示引脚状态,所以其访问处理函数为:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    void handleRoot() {
    String displayPinState; // 存储按键状态的字符串变量

    if(pinState == HIGH){ // 当按键引脚D3为高电平
    displayPinState = "Button State: HIGH"; // 字符串赋值高电平信息
    } else { // 当按键引脚D3为低电平
    displayPinState = "Button State: LOW"; // 字符串赋值低电平信息
    }
    esp8266_server.send(200, "text/plain", displayPinState);
    // 向浏览器发送按键状态信息
    }
    \n
  2. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

bool pinState;

void setup(){
Serial.begin(9600);

pinMode(D3, INPUT_PULLUP); //上拉输入模式

/*服务器配置*/
server.begin();
server.on("/", HTTP_GET, handleRoot);
server.onNotFound(handleNotFound);

/*Wifi配置*/
wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(800);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}

void loop(){
server.handleClient();
pinState = digitalRead(D3);
}

void handleRoot(){
String displayPinState;
if(pinState == HIGH){ // 当按键引脚D3为高电平
displayPinState = "Button State: HIGH"; // 字符串赋值高电平信息
} else { // 当按键引脚D3为低电平
displayPinState = "Button State: LOW"; // 字符串赋值低电平信息
}
server.send(200, "text/plain", displayPinState);
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}
\n
\n

注意:引脚状态改变后需要手动刷新网页,网页显示才会改变

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

1、建立基本网络服务器

    \n
  1. 导入库文件 ESP8266WebServer.h

    \n
  2. \n
  3. 创建服务器对象

    \n
    1
    ESP8266WebServer server(80);  //80是端口号
    \n
  4. \n
  5. 启动服务器

    \n
    1
    server.begin();
    \n
  6. \n
  7. 服务器访问配置

    \n
    1
    2
    3
    4
    5
    // 访问根节点时,调用处理函数 handleRoot
    server.on("/", handleRoot);

    //访问不到,即404时,调用处理函数 handleNotFound
    server.onNotFound(handleNotFound);
    \n
  8. \n
  9. 访问处理函数

    \n
    1
    2
    3
    void handleRoot(){
    server.send(200, "text/plain", "Hello from ESP8266");
    }
    \n
    1
    2
    3
    void handleNotFound(){
    server.send(404, "text/plain", "404: Not found");
    }
    \n
  10. \n
\n
    \n
  1. 循环监听端口

    \n
    1
    2
    3
    void loop(){
    server.handleClient(); //循环监听客户端访问情况
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

void setup(){
Serial.begin(9600);
server.begin();
server.on("/", handleRoot);
server.onNotFound(handleNotFound);

wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(600);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());

}

//监听客户端
void loop(){
server.handleClient();
}

//访问处理函数
void handleRoot(){
server.send(200, "text/plain", "Hello esp8266");
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}
\n

2、通过网络服务器实现开发板控制

通过网页控制nodeMCU开发板小灯亮灭

\n

步骤1、2、3、6不变,只是第3、4步的服务器访问配置和访问处理函数有些许改动

\n
    \n
  1. 服务器访问配置

    \n
    1
    2
    3
    4
    5
    6
    server.on("/", HTTP_GET, handleRoot);

    /*添加这个控制LED的网页, 使用post方式发送http请求*/
    server.on("/LED", HTTP_POST, handleLED);

    server.onNotFound(handleNotFound);
    \n
  2. \n
  3. 访问处理函数

    \n
    1
    2
    3
    4

    void handleRoot(){
    server.send(200, "text/html", "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>");
    }
    \n
    \n

    注意这句html代码:

    \n
    1
    2
    3
    <form action="/LED" method="POST">
    <input type="submit" value="Toggle LED">
    </form>
    \n

    这会生成一个按钮,点击这个按钮会把数据发到”/LED”页面

    \n
    \n
  4. \n
\n

下面是“/LED”页面的处理函数:

\n
1
2
3
4
5
6
7
void handleLED(){
static bool LEDState = LOW;//记录LED此时亮灭状态
LEDState = !LEDState;
digitalWrite(LED_BUILTIN, LEDState);
server.sendHeader("Location", "/");
server.send(303); //303表示将网页重定向
}
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

void setup(){
Serial.begin(9600);

pinMode(LED_BUILTIN, OUTPUT); //设置内置LED引脚为输出模式

/*服务器配置*/
server.begin();
server.on("/", HTTP_GET, handleRoot);
server.on("/LED", HTTP_POST, handleLED);
server.onNotFound(handleNotFound);

/*Wifi配置*/
wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(600);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}

void loop(){
server.handleClient();
}

void handleRoot(){
server.send(200, "text/html", "<form action=\\"/LED\\" method=\\"POST\\"><input type=\\"submit\\" value=\\"Toggle LED\\"></form>");
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}

void handleLED(){
static bool LEDState = LOW; //记录LED此时亮灭状态
LEDState = !LEDState;
digitalWrite(LED_BUILTIN, LEDState);
server.sendHeader("Location", "/");
server.send(303); //303表示将网页重定向
}
\n

3、将开发板引脚状态发送到终端网页

将引脚D3(已经与flash按键相连,按键按下为低电平)的电平状态显示到网页中

\n
    \n
  1. 首先在开始时将D3引脚设置为上拉输入模式

    \n
    1
    pinMode(D3, INPUT_PULLUP); 
    \n
  2. \n
  3. 循环读取引脚状态 digitalRead()

    \n
    1
    2
    3
    4
    5
    bool pinState;
    void loop(){
    pinState = digitalRead(D3); // 获取引脚状态
    //......
    }
    \n
  4. \n
\n
    \n
  1. 要在根节点下显示引脚状态,所以其访问处理函数为:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    void handleRoot() {
    String displayPinState; // 存储按键状态的字符串变量

    if(pinState == HIGH){ // 当按键引脚D3为高电平
    displayPinState = "Button State: HIGH"; // 字符串赋值高电平信息
    } else { // 当按键引脚D3为低电平
    displayPinState = "Button State: LOW"; // 字符串赋值低电平信息
    }
    esp8266_server.send(200, "text/plain", displayPinState);
    // 向浏览器发送按键状态信息
    }
    \n
  2. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>

ESP8266WiFiMulti wifiMulti;
ESP8266WebServer server(80); //80是端口号

bool pinState;

void setup(){
Serial.begin(9600);

pinMode(D3, INPUT_PULLUP); //上拉输入模式

/*服务器配置*/
server.begin();
server.on("/", HTTP_GET, handleRoot);
server.onNotFound(handleNotFound);

/*Wifi配置*/
wifiMulti.addAP("RBook", "17191719");
wifiMulti.addAP("bbb", "12345678");
wifiMulti.addAP("ccc", "12345678");

int i = 0;
while (wifiMulti.run() != WL_CONNECTED){
delay(800);
Serial.print(++i);
Serial.print(" ");
}

Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}

void loop(){
server.handleClient();
pinState = digitalRead(D3);
}

void handleRoot(){
String displayPinState;
if(pinState == HIGH){ // 当按键引脚D3为高电平
displayPinState = "Button State: HIGH"; // 字符串赋值高电平信息
} else { // 当按键引脚D3为低电平
displayPinState = "Button State: LOW"; // 字符串赋值低电平信息
}
server.send(200, "text/plain", displayPinState);
}

void handleNotFound(){
server.send(404, "text/plain", "404 not found");
}
\n
\n

注意:引脚状态改变后需要手动刷新网页,网页显示才会改变

\n
\n"},{"title":"esp8266(4)-NodeMCU作为客户端使用","abbrlink":"d150c3f1","date":"2021-08-03T03:21:00.000Z","description":"esp8266作为客户端如何连接服务器","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\nESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是`WiFiClient`库和`ESP8266HTTPClient`库。\n\n【注意】\n\n- `WiFiClient` 被声明在 `` 中(这种方式实现Client更复杂,但灵活)\n\n- 而`ESP8266HTTPClient`被单独声明在 ``(这种方式实现Client更简单,但很多功能不能根据自己意愿来定制实现)\n\n\n\n\n\n## 1、ESP8266HTTPClient 实现\n\n1. 连接WiFi,具体实现看第一篇文章\n\n2. 连接WiFi后,写一个函数来访问服务器\n\n3. 函数设计,五个步骤:\n\n - 创建客户端对象\n - 配置访问地址url\n - 发送get(post)请求\n - 请求成功:处理返回信息\n - 请求失败:(任意)\n - 结束请求\n\n ```c\n // 发送HTTP请求并且将服务器响应通过串口输出\n void httpClientRequest(){\n \n //1 创建 HTTPClient 对象\n HTTPClient httpClient;\n \n //2 通过begin函数配置请求地址\n httpClient.begin(URL); \n \n //3 通过GET函数启动连接并发送HTTP请求\n int httpCode = httpClient.GET();\n \n //4 处理服务器返回信息\n if (httpCode == HTTP_CODE_OK) {\n // 使用getString函数获取服务器响应体内容\n String responsePayload = httpClient.getString();\n //...\n } else {\n //...\n }\n \n //5 关闭ESP8266与服务器连接\n httpClient.end();\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n\nvoid setup() {\n String url = \"http://www.example.com\";\n \n Serial.begin(115200);\n \n WiFi.mode(WIFI_STA);\n WiFi.begin(\"RBook\", \"17191719\");\n\n for(int i=0; WiFi.status() != WL_CONNECTED; i++){\n delay(1000);\n Serial.printf(\"%d \", i);\n }\n Serial.print(\"\\r\\nwifi is connected\\r\\n\");\n\n getResponse(url);\n \n}\n\nvoid loop() {\n // put your main code here, to run repeatedly:\n\n}\n\n//五个步骤进行发送请求并获取数据\nvoid getResponse(String url){\n \n HTTPClient httpClient; // 1\n\n httpClient.begin(url); // 2\n\n int httpCode = httpClient.GET(); // 3\n\n // 4\n if(httpCode == HTTP_CODE_OK){\n Serial.printf(\"httpCode: %d Success......\\n\", httpCode);\n String res = httpClient.getString();\n Serial.println(\"======================\");\n Serial.println(res);\n Serial.println(\"======================\");\n }\n else {\n Serial.printf(\"httpCode: %d Failed......\\n\", httpCode);\n }\n\n // 5\n httpClient.end();\n \n}\n\n```\n\n##### 【注意】\n\n> 转义字符 `\\r` 表示将光标移动到本行开头(继续输出会覆盖本行内容)\n>\n> `\\r\\n` 表示将光标移到开头再换行(不会覆盖本行内容),一般与 `\\n` 效果相同\n\n\n\n## 2、WiFiClient 实现\n\n1. 创建对象\n2. 连接服务器\n - 连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接\n - 连接失败:断开连接\n\n```c\nvoid wifiClientRequest(String url){\n // 建立WiFi客户端对象\n WiFiClient client; \n\n // 建立字符串,用于HTTP请求\n String httpRequest = String(\"GET /\") + \" HTTP/1.1\\r\\n\" +\n \"url: \" + url + \"\\r\\n\" +\n \"Connection: close\\r\\n\" +\n \"\\r\\n\";\n\n // 连接网络服务器\n if (client.connect(url, httpPort)){ \t//连接成功connect会返回true\n \n client.print(httpRequest); // 向服务器发送HTTP请求\n\n // 通过串口输出网络服务器响应信息 \n while (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //一行一行读取\n Serial.println(line);\n }\n }\n\n client.stop();\t\t// 断开与服务器的连接\n\n } else{ \n client.stop();\t\t// 如果连接不成功则断开连接\n }\n```\n\n\n\n## ","source":"_posts/esp8266/4-NodeMCU作为客户端使用.md","raw":"---\ntitle: esp8266(4)-NodeMCU作为客户端使用\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: d150c3f1\ndate: 2021-08-03 11:21:00\ndescription: esp8266作为客户端如何连接服务器\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\nESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是`WiFiClient`库和`ESP8266HTTPClient`库。\n\n【注意】\n\n- `WiFiClient` 被声明在 `` 中(这种方式实现Client更复杂,但灵活)\n\n- 而`ESP8266HTTPClient`被单独声明在 ``(这种方式实现Client更简单,但很多功能不能根据自己意愿来定制实现)\n\n\n\n\n\n## 1、ESP8266HTTPClient 实现\n\n1. 连接WiFi,具体实现看第一篇文章\n\n2. 连接WiFi后,写一个函数来访问服务器\n\n3. 函数设计,五个步骤:\n\n - 创建客户端对象\n - 配置访问地址url\n - 发送get(post)请求\n - 请求成功:处理返回信息\n - 请求失败:(任意)\n - 结束请求\n\n ```c\n // 发送HTTP请求并且将服务器响应通过串口输出\n void httpClientRequest(){\n \n //1 创建 HTTPClient 对象\n HTTPClient httpClient;\n \n //2 通过begin函数配置请求地址\n httpClient.begin(URL); \n \n //3 通过GET函数启动连接并发送HTTP请求\n int httpCode = httpClient.GET();\n \n //4 处理服务器返回信息\n if (httpCode == HTTP_CODE_OK) {\n // 使用getString函数获取服务器响应体内容\n String responsePayload = httpClient.getString();\n //...\n } else {\n //...\n }\n \n //5 关闭ESP8266与服务器连接\n httpClient.end();\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n\nvoid setup() {\n String url = \"http://www.example.com\";\n \n Serial.begin(115200);\n \n WiFi.mode(WIFI_STA);\n WiFi.begin(\"RBook\", \"17191719\");\n\n for(int i=0; WiFi.status() != WL_CONNECTED; i++){\n delay(1000);\n Serial.printf(\"%d \", i);\n }\n Serial.print(\"\\r\\nwifi is connected\\r\\n\");\n\n getResponse(url);\n \n}\n\nvoid loop() {\n // put your main code here, to run repeatedly:\n\n}\n\n//五个步骤进行发送请求并获取数据\nvoid getResponse(String url){\n \n HTTPClient httpClient; // 1\n\n httpClient.begin(url); // 2\n\n int httpCode = httpClient.GET(); // 3\n\n // 4\n if(httpCode == HTTP_CODE_OK){\n Serial.printf(\"httpCode: %d Success......\\n\", httpCode);\n String res = httpClient.getString();\n Serial.println(\"======================\");\n Serial.println(res);\n Serial.println(\"======================\");\n }\n else {\n Serial.printf(\"httpCode: %d Failed......\\n\", httpCode);\n }\n\n // 5\n httpClient.end();\n \n}\n\n```\n\n##### 【注意】\n\n> 转义字符 `\\r` 表示将光标移动到本行开头(继续输出会覆盖本行内容)\n>\n> `\\r\\n` 表示将光标移到开头再换行(不会覆盖本行内容),一般与 `\\n` 效果相同\n\n\n\n## 2、WiFiClient 实现\n\n1. 创建对象\n2. 连接服务器\n - 连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接\n - 连接失败:断开连接\n\n```c\nvoid wifiClientRequest(String url){\n // 建立WiFi客户端对象\n WiFiClient client; \n\n // 建立字符串,用于HTTP请求\n String httpRequest = String(\"GET /\") + \" HTTP/1.1\\r\\n\" +\n \"url: \" + url + \"\\r\\n\" +\n \"Connection: close\\r\\n\" +\n \"\\r\\n\";\n\n // 连接网络服务器\n if (client.connect(url, httpPort)){ \t//连接成功connect会返回true\n \n client.print(httpRequest); // 向服务器发送HTTP请求\n\n // 通过串口输出网络服务器响应信息 \n while (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //一行一行读取\n Serial.println(line);\n }\n }\n\n client.stop();\t\t// 断开与服务器的连接\n\n } else{ \n client.stop();\t\t// 如果连接不成功则断开连接\n }\n```\n\n\n\n## ","slug":"esp8266/4-NodeMCU作为客户端使用","published":1,"updated":"2021-09-11T07:54:22.484Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pu003zakve3wlwdsem","content":"

ESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是WiFiClient库和ESP8266HTTPClient库。

\n

【注意】

\n
    \n
  • WiFiClient 被声明在 <ESP8266WiFi.h> 中(这种方式实现Client更复杂,但灵活)

    \n
  • \n
  • ESP8266HTTPClient被单独声明在 <ESP8266HTTPClient.h>(这种方式实现Client更简单,但很多功能不能根据自己意愿来定制实现)

    \n
  • \n
\n

1、ESP8266HTTPClient 实现

    \n
  1. 连接WiFi,具体实现看第一篇文章

    \n
  2. \n
  3. 连接WiFi后,写一个函数来访问服务器

    \n
  4. \n
  5. 函数设计,五个步骤:

    \n
      \n
    • 创建客户端对象
    • \n
    • 配置访问地址url
    • \n
    • 发送get(post)请求
        \n
      • 请求成功:处理返回信息
      • \n
      • 请求失败:(任意)
      • \n
      \n
    • \n
    • 结束请求
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 发送HTTP请求并且将服务器响应通过串口输出
    void httpClientRequest(){

    //1 创建 HTTPClient 对象
    HTTPClient httpClient;

    //2 通过begin函数配置请求地址
    httpClient.begin(URL);

    //3 通过GET函数启动连接并发送HTTP请求
    int httpCode = httpClient.GET();

    //4 处理服务器返回信息
    if (httpCode == HTTP_CODE_OK) {
    // 使用getString函数获取服务器响应体内容
    String responsePayload = httpClient.getString();
    //...
    } else {
    //...
    }

    //5 关闭ESP8266与服务器连接
    httpClient.end();
    }
    \n
  6. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

void setup() {
String url = "http://www.example.com";

Serial.begin(115200);

WiFi.mode(WIFI_STA);
WiFi.begin("RBook", "17191719");

for(int i=0; WiFi.status() != WL_CONNECTED; i++){
delay(1000);
Serial.printf("%d ", i);
}
Serial.print("\\r\\nwifi is connected\\r\\n");

getResponse(url);

}

void loop() {
// put your main code here, to run repeatedly:

}

//五个步骤进行发送请求并获取数据
void getResponse(String url){

HTTPClient httpClient; // 1

httpClient.begin(url); // 2

int httpCode = httpClient.GET(); // 3

// 4
if(httpCode == HTTP_CODE_OK){
Serial.printf("httpCode: %d Success......\\n", httpCode);
String res = httpClient.getString();
Serial.println("======================");
Serial.println(res);
Serial.println("======================");
}
else {
Serial.printf("httpCode: %d Failed......\\n", httpCode);
}

// 5
httpClient.end();

}

\n
【注意】
\n

转义字符 \\r 表示将光标移动到本行开头(继续输出会覆盖本行内容)

\n

\\r\\n 表示将光标移到开头再换行(不会覆盖本行内容),一般与 \\n 效果相同

\n
\n

2、WiFiClient 实现

    \n
  1. 创建对象
  2. \n
  3. 连接服务器
      \n
    • 连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接
    • \n
    • 连接失败:断开连接
    • \n
    \n
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void wifiClientRequest(String url){
// 建立WiFi客户端对象
WiFiClient client;

// 建立字符串,用于HTTP请求
String httpRequest = String("GET /") + " HTTP/1.1\\r\\n" +
"url: " + url + "\\r\\n" +
"Connection: close\\r\\n" +
"\\r\\n";

// 连接网络服务器
if (client.connect(url, httpPort)){ \t//连接成功connect会返回true

client.print(httpRequest); // 向服务器发送HTTP请求

// 通过串口输出网络服务器响应信息
while (client.connected() || client.available()){
if (client.available()){
String line = client.readStringUntil('\\n'); //一行一行读取
Serial.println(line);
}
}

client.stop();\t\t// 断开与服务器的连接

} else{
client.stop();\t\t// 如果连接不成功则断开连接
}
\n

","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

ESP8266-Arduino库中有两个库用于控制ESP8266与网络服务器进行通讯。他们是WiFiClient库和ESP8266HTTPClient库。

\n

【注意】

\n
    \n
  • WiFiClient 被声明在 <ESP8266WiFi.h> 中(这种方式实现Client更复杂,但灵活)

    \n
  • \n
  • ESP8266HTTPClient被单独声明在 <ESP8266HTTPClient.h>(这种方式实现Client更简单,但很多功能不能根据自己意愿来定制实现)

    \n
  • \n
\n

1、ESP8266HTTPClient 实现

    \n
  1. 连接WiFi,具体实现看第一篇文章

    \n
  2. \n
  3. 连接WiFi后,写一个函数来访问服务器

    \n
  4. \n
  5. 函数设计,五个步骤:

    \n
      \n
    • 创建客户端对象
    • \n
    • 配置访问地址url
    • \n
    • 发送get(post)请求
        \n
      • 请求成功:处理返回信息
      • \n
      • 请求失败:(任意)
      • \n
      \n
    • \n
    • 结束请求
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 发送HTTP请求并且将服务器响应通过串口输出
    void httpClientRequest(){

    //1 创建 HTTPClient 对象
    HTTPClient httpClient;

    //2 通过begin函数配置请求地址
    httpClient.begin(URL);

    //3 通过GET函数启动连接并发送HTTP请求
    int httpCode = httpClient.GET();

    //4 处理服务器返回信息
    if (httpCode == HTTP_CODE_OK) {
    // 使用getString函数获取服务器响应体内容
    String responsePayload = httpClient.getString();
    //...
    } else {
    //...
    }

    //5 关闭ESP8266与服务器连接
    httpClient.end();
    }
    \n
  6. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

void setup() {
String url = "http://www.example.com";

Serial.begin(115200);

WiFi.mode(WIFI_STA);
WiFi.begin("RBook", "17191719");

for(int i=0; WiFi.status() != WL_CONNECTED; i++){
delay(1000);
Serial.printf("%d ", i);
}
Serial.print("\\r\\nwifi is connected\\r\\n");

getResponse(url);

}

void loop() {
// put your main code here, to run repeatedly:

}

//五个步骤进行发送请求并获取数据
void getResponse(String url){

HTTPClient httpClient; // 1

httpClient.begin(url); // 2

int httpCode = httpClient.GET(); // 3

// 4
if(httpCode == HTTP_CODE_OK){
Serial.printf("httpCode: %d Success......\\n", httpCode);
String res = httpClient.getString();
Serial.println("======================");
Serial.println(res);
Serial.println("======================");
}
else {
Serial.printf("httpCode: %d Failed......\\n", httpCode);
}

// 5
httpClient.end();

}

\n
【注意】
\n

转义字符 \\r 表示将光标移动到本行开头(继续输出会覆盖本行内容)

\n

\\r\\n 表示将光标移到开头再换行(不会覆盖本行内容),一般与 \\n 效果相同

\n
\n

2、WiFiClient 实现

    \n
  1. 创建对象
  2. \n
  3. 连接服务器
      \n
    • 连接成功:(1)发送请求(2)获取并处理服务器响应的数据(3)断开与服务器的连接
    • \n
    • 连接失败:断开连接
    • \n
    \n
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void wifiClientRequest(String url){
// 建立WiFi客户端对象
WiFiClient client;

// 建立字符串,用于HTTP请求
String httpRequest = String("GET /") + " HTTP/1.1\\r\\n" +
"url: " + url + "\\r\\n" +
"Connection: close\\r\\n" +
"\\r\\n";

// 连接网络服务器
if (client.connect(url, httpPort)){ \t//连接成功connect会返回true

client.print(httpRequest); // 向服务器发送HTTP请求

// 通过串口输出网络服务器响应信息
while (client.connected() || client.available()){
if (client.available()){
String line = client.readStringUntil('\\n'); //一行一行读取
Serial.println(line);
}
}

client.stop();\t\t// 断开与服务器的连接

} else{
client.stop();\t\t// 如果连接不成功则断开连接
}
\n

"},{"title":"esp8266(5)-Stream数据流","abbrlink":"2d7f83ef","date":"2021-08-03T05:10:00.000Z","description":"esp8266读取串口传入的数据、文件系统的数据、服务器传来的数据","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n# Stream数据流\n\n## 1、串口收发数据\n\n```c\nvoid setup() {\n // 启动串口通讯\n Serial.begin(9600); \n Serial.println();\n}\n \nvoid loop() { \n if (Serial.available()){ // 当串口接收到信息后\n String serialData = Serial.readString(); // 将接收到的信息存储于serialData变量\n Serial.print(serialData); \n }\n}\n```\n\n使用`Serial.available`来判断ESP8266开发板是否接收到串口数据\n\n实际上,ESP8266开发板通过串口收发的数据通过Stream进行的\n\n> 下面的程序将演示:\n>\n> 当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。\n>\n> ```c\n> void setup() {\n> Serial.begin(9600);\n> Serial.println(\"\");\n> Serial.println(\"Please enter input...\");\n> }\n> \n> void loop() {\n> while(Serial.available()){\n> if(Serial.find(\"ok\")){\n> Serial.println(\"Found ok in user input.\");\n> \n> int serialParseInt = Serial.parseInt();\n> Serial.print(\"serialParseInt = \");\n> Serial.println(serialParseInt);\n> \n> String serialInput = Serial.readString();\n> Serial.print(\"serialInput = \");\n> Serial.println(serialInput);\n> }\n> }\n> }\n> ```\n>\n> \n\n\n\n\n\n## 2、使用Stream方式来读取服务器响应的信息\n\n(具体看第4篇文章第2点的代码)\n\n```c\nwhile (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //读取返回的数据\n Serial.println(line);\n }\n}\n```\n\n\n\n## 3、使用Stream方式来读取文件内容\n\n\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); // 以“r”模式再次打开闪存文件\nSerial.println(f.readString());\t\t\t\t// 读取文件内容\n```\n\n","source":"_posts/esp8266/5-Stream数据流.md","raw":"---\ntitle: esp8266(5)-Stream数据流\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 2d7f83ef\ndate: 2021-08-03 13:10:00\ndescription: esp8266读取串口传入的数据、文件系统的数据、服务器传来的数据\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n# Stream数据流\n\n## 1、串口收发数据\n\n```c\nvoid setup() {\n // 启动串口通讯\n Serial.begin(9600); \n Serial.println();\n}\n \nvoid loop() { \n if (Serial.available()){ // 当串口接收到信息后\n String serialData = Serial.readString(); // 将接收到的信息存储于serialData变量\n Serial.print(serialData); \n }\n}\n```\n\n使用`Serial.available`来判断ESP8266开发板是否接收到串口数据\n\n实际上,ESP8266开发板通过串口收发的数据通过Stream进行的\n\n> 下面的程序将演示:\n>\n> 当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。\n>\n> ```c\n> void setup() {\n> Serial.begin(9600);\n> Serial.println(\"\");\n> Serial.println(\"Please enter input...\");\n> }\n> \n> void loop() {\n> while(Serial.available()){\n> if(Serial.find(\"ok\")){\n> Serial.println(\"Found ok in user input.\");\n> \n> int serialParseInt = Serial.parseInt();\n> Serial.print(\"serialParseInt = \");\n> Serial.println(serialParseInt);\n> \n> String serialInput = Serial.readString();\n> Serial.print(\"serialInput = \");\n> Serial.println(serialInput);\n> }\n> }\n> }\n> ```\n>\n> \n\n\n\n\n\n## 2、使用Stream方式来读取服务器响应的信息\n\n(具体看第4篇文章第2点的代码)\n\n```c\nwhile (client.connected() || client.available()){ \n if (client.available()){\n String line = client.readStringUntil('\\n'); //读取返回的数据\n Serial.println(line);\n }\n}\n```\n\n\n\n## 3、使用Stream方式来读取文件内容\n\n\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); // 以“r”模式再次打开闪存文件\nSerial.println(f.readString());\t\t\t\t// 读取文件内容\n```\n\n","slug":"esp8266/5-Stream数据流","published":1,"updated":"2021-09-11T07:54:38.317Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pv0042akve76zr0o90","content":"

Stream数据流

1、串口收发数据

1
2
3
4
5
6
7
8
9
10
11
12
void setup() {
// 启动串口通讯
Serial.begin(9600);
Serial.println();
}

void loop() {
if (Serial.available()){ // 当串口接收到信息后
String serialData = Serial.readString(); // 将接收到的信息存储于serialData变量
Serial.print(serialData);
}
}
\n

使用Serial.available来判断ESP8266开发板是否接收到串口数据

\n

实际上,ESP8266开发板通过串口收发的数据通过Stream进行的

\n
\n

下面的程序将演示:

\n

当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void setup() {
Serial.begin(9600);
Serial.println("");
Serial.println("Please enter input...");
}

void loop() {
while(Serial.available()){
if(Serial.find("ok")){
Serial.println("Found ok in user input.");

int serialParseInt = Serial.parseInt();
Serial.print("serialParseInt = ");
Serial.println(serialParseInt);

String serialInput = Serial.readString();
Serial.print("serialInput = ");
Serial.println(serialInput);
}
}
}
\n
\n

2、使用Stream方式来读取服务器响应的信息

(具体看第4篇文章第2点的代码)

\n
1
2
3
4
5
6
while (client.connected() || client.available()){ 
if (client.available()){
String line = client.readStringUntil('\\n'); //读取返回的数据
Serial.println(line);
}
}
\n

3、使用Stream方式来读取文件内容

1
2
File f = SPIFFS.open(file_name, "r");     // 以“r”模式再次打开闪存文件
Serial.println(f.readString());\t\t\t\t// 读取文件内容
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Stream数据流

1、串口收发数据

1
2
3
4
5
6
7
8
9
10
11
12
void setup() {
// 启动串口通讯
Serial.begin(9600);
Serial.println();
}

void loop() {
if (Serial.available()){ // 当串口接收到信息后
String serialData = Serial.readString(); // 将接收到的信息存储于serialData变量
Serial.print(serialData);
}
}
\n

使用Serial.available来判断ESP8266开发板是否接收到串口数据

\n

实际上,ESP8266开发板通过串口收发的数据通过Stream进行的

\n
\n

下面的程序将演示:

\n

当ESP8266找到了find函数所指定的参数“ok”后,随即在后续接收到的数据中查找数字信息。一旦找到数字,则通过串口监视器输出。接下来串口监视器还将输出找到数字后剩余的串口输入信息是什么。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void setup() {
Serial.begin(9600);
Serial.println("");
Serial.println("Please enter input...");
}

void loop() {
while(Serial.available()){
if(Serial.find("ok")){
Serial.println("Found ok in user input.");

int serialParseInt = Serial.parseInt();
Serial.print("serialParseInt = ");
Serial.println(serialParseInt);

String serialInput = Serial.readString();
Serial.print("serialInput = ");
Serial.println(serialInput);
}
}
}
\n
\n

2、使用Stream方式来读取服务器响应的信息

(具体看第4篇文章第2点的代码)

\n
1
2
3
4
5
6
while (client.connected() || client.available()){ 
if (client.available()){
String line = client.readStringUntil('\\n'); //读取返回的数据
Serial.println(line);
}
}
\n

3、使用Stream方式来读取文件内容

1
2
File f = SPIFFS.open(file_name, "r");     // 以“r”模式再次打开闪存文件
Serial.println(f.readString());\t\t\t\t// 读取文件内容
\n"},{"title":"esp8266(3)-闪存文件系统","abbrlink":"2080031a","date":"2021-08-03T01:00:00.000Z","description":"esp8266的文件系统使用(向闪存中写入信息、读取信息)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n## 1、文件基本操作\n\nESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS\n\n\n\n代码中需要用到头文件`` 包含的类`SPIFFS`\n\n下面教你如何打开一个文件并向里面写如信息\n\n1. 格式化SPIFFS\n\n ```c++\n SPIFFS.format(); \n ```\n\n2. 启动SPIFFS\n\n ```c++\n SPIFFS.begin(); //该函数会返回一个bool型结果,启动成功返回true,否则为false\n ```\n\n3. 用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)\n\n ```c++\n //以写入的方式打开一个文件,filename为文件路径\n String file_name = \"/folder/notes.txt\"\n File dataFile = SPIFFS.open(file_name, \"w\");\t\t\n dataFile.println(\"Hello World\"); // 向dataFile写入字符串信息\n dataFile.close(); // 关闭文件\n ```\n\n \n\n完整code:\n\n```c++\n#include \n\nvoid setup(){\n Serial.begin(9600);\n \n String filename = \"/folder/notes.txt\";\n SPIFFS.format(); \n if(SPIFFS.begin()) Serial.println(\"SPIFFS Started...\");\n else Serial.println(\"SPIFFS Failed to Start...\");\n\n File f = SPIFFS.open(filename, \"w\");\n f.println(\"Hello esp8266 world\");\n f.close();\n Serial.println(\"Finish writing the file...\");\n \n}\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n### SPIFFS基于文件的基本操作\n\n```c\nvoid SPIFFS.format(); //格式化闪存文件系统 【注意:格式化文件系统需要耗费一定时间】\n\nbool SPIFFS.exists(String s); //是否存在文件名为s的文件(准确说s是文件的路径)\nbool SPIFFS.remove(String s); //删除文件s,返回是否删除成功\n```\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); //以读的方式打开一个文件\n\n//打印文件中所有内容\nfor(int i=0; i 注意:不论以何种方式打开文件,最后都要记得关闭文件\n\n\n\n### 基于目录的操作\n\n\n\n获取一个目录对象 `openDir()`:\n\n```c\nString folder_name = \"/folder\"; //被读取的文件夹\nDir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象\n\n// dir.next()可以看作一个指针,每循环一次就会指向下一个元素\nwhile (dir.next()) { \n Serial.println(dir.fileName()); // 输出文件名\n}\n```\n\n\n\n\n\n\n\n### 闪存文件系统的基本信息\n\n```c\nFSInfo fs_info; // 创建一个基本信息的对象\n\n// 写入闪存文件系统信息到fs_info\nSPIFFS.info(fs_info);\n\nSerial.print(fs_info.totalBytes); // 可用空间总和(Bytes)\nSerial.print(fs_info.usedBytes); // 已经用掉的空间(Bytes)\n\n// 最大文件名字符限制(含路径和'\\0')\nSerial.println(fs_info.maxPathLength);\n\n// 最多允许打开文件数量\nSerial.println(fs_info.maxOpenFiles);\n\n// 存储块大小\nSerial.println(fs_info.blockSize);\n\n// 存储页大小\nSerial.println(fs_info.pageSize);\n```\n\n\n\n\n\n## 2、通过Arduino IDE向闪存文件系统上传文件\n\n\n\n看太极创客这篇[文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/upload-files/)\n\n##### 【注意】\n\n1. 上传文件前,把【串口监视器】关闭\n2. 之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了\n\n\n\n## 3、使用闪存系统配置功能更丰富的网络服务器\n\n[太极创客文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/spiffs-web-server/)\n\n","source":"_posts/esp8266/3-闪存文件系统.md","raw":"---\ntitle: esp8266(3)-闪存文件系统\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 2080031a\ndate: 2021-08-03 09:00:00\ndescription: esp8266的文件系统使用(向闪存中写入信息、读取信息)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n## 1、文件基本操作\n\nESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS\n\n\n\n代码中需要用到头文件`` 包含的类`SPIFFS`\n\n下面教你如何打开一个文件并向里面写如信息\n\n1. 格式化SPIFFS\n\n ```c++\n SPIFFS.format(); \n ```\n\n2. 启动SPIFFS\n\n ```c++\n SPIFFS.begin(); //该函数会返回一个bool型结果,启动成功返回true,否则为false\n ```\n\n3. 用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)\n\n ```c++\n //以写入的方式打开一个文件,filename为文件路径\n String file_name = \"/folder/notes.txt\"\n File dataFile = SPIFFS.open(file_name, \"w\");\t\t\n dataFile.println(\"Hello World\"); // 向dataFile写入字符串信息\n dataFile.close(); // 关闭文件\n ```\n\n \n\n完整code:\n\n```c++\n#include \n\nvoid setup(){\n Serial.begin(9600);\n \n String filename = \"/folder/notes.txt\";\n SPIFFS.format(); \n if(SPIFFS.begin()) Serial.println(\"SPIFFS Started...\");\n else Serial.println(\"SPIFFS Failed to Start...\");\n\n File f = SPIFFS.open(filename, \"w\");\n f.println(\"Hello esp8266 world\");\n f.close();\n Serial.println(\"Finish writing the file...\");\n \n}\n\nvoid loop(){\n \n}\n```\n\n\n\n\n\n### SPIFFS基于文件的基本操作\n\n```c\nvoid SPIFFS.format(); //格式化闪存文件系统 【注意:格式化文件系统需要耗费一定时间】\n\nbool SPIFFS.exists(String s); //是否存在文件名为s的文件(准确说s是文件的路径)\nbool SPIFFS.remove(String s); //删除文件s,返回是否删除成功\n```\n\n```c\nFile f = SPIFFS.open(file_name, \"r\"); //以读的方式打开一个文件\n\n//打印文件中所有内容\nfor(int i=0; i 注意:不论以何种方式打开文件,最后都要记得关闭文件\n\n\n\n### 基于目录的操作\n\n\n\n获取一个目录对象 `openDir()`:\n\n```c\nString folder_name = \"/folder\"; //被读取的文件夹\nDir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象\n\n// dir.next()可以看作一个指针,每循环一次就会指向下一个元素\nwhile (dir.next()) { \n Serial.println(dir.fileName()); // 输出文件名\n}\n```\n\n\n\n\n\n\n\n### 闪存文件系统的基本信息\n\n```c\nFSInfo fs_info; // 创建一个基本信息的对象\n\n// 写入闪存文件系统信息到fs_info\nSPIFFS.info(fs_info);\n\nSerial.print(fs_info.totalBytes); // 可用空间总和(Bytes)\nSerial.print(fs_info.usedBytes); // 已经用掉的空间(Bytes)\n\n// 最大文件名字符限制(含路径和'\\0')\nSerial.println(fs_info.maxPathLength);\n\n// 最多允许打开文件数量\nSerial.println(fs_info.maxOpenFiles);\n\n// 存储块大小\nSerial.println(fs_info.blockSize);\n\n// 存储页大小\nSerial.println(fs_info.pageSize);\n```\n\n\n\n\n\n## 2、通过Arduino IDE向闪存文件系统上传文件\n\n\n\n看太极创客这篇[文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/upload-files/)\n\n##### 【注意】\n\n1. 上传文件前,把【串口监视器】关闭\n2. 之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了\n\n\n\n## 3、使用闪存系统配置功能更丰富的网络服务器\n\n[太极创客文章](http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/spiffs-web-server/)\n\n","slug":"esp8266/3-闪存文件系统","published":1,"updated":"2021-09-11T07:55:24.239Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6pw0044akveh5zqdjte","content":"

1、文件基本操作

ESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS

\n

代码中需要用到头文件<FS.h> 包含的类SPIFFS

\n

下面教你如何打开一个文件并向里面写如信息

\n
    \n
  1. 格式化SPIFFS

    \n
    1
    SPIFFS.format(); 
    \n
  2. \n
  3. 启动SPIFFS

    \n
    1
    SPIFFS.begin();   //该函数会返回一个bool型结果,启动成功返回true,否则为false
    \n
  4. \n
  5. 用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)

    \n
    1
    2
    3
    4
    5
    //以写入的方式打开一个文件,filename为文件路径
    String file_name = "/folder/notes.txt"
    File dataFile = SPIFFS.open(file_name, "w");\t\t
    dataFile.println("Hello World"); // 向dataFile写入字符串信息
    dataFile.close(); // 关闭文件
    \n
  6. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <FS.h>

void setup(){
Serial.begin(9600);

String filename = "/folder/notes.txt";
SPIFFS.format();
if(SPIFFS.begin()) Serial.println("SPIFFS Started...");
else Serial.println("SPIFFS Failed to Start...");

File f = SPIFFS.open(filename, "w");
f.println("Hello esp8266 world");
f.close();
Serial.println("Finish writing the file...");

}

void loop(){

}
\n

SPIFFS基于文件的基本操作

1
2
3
4
void SPIFFS.format();   //格式化闪存文件系统 【注意:格式化文件系统需要耗费一定时间】

bool SPIFFS.exists(String s); //是否存在文件名为s的文件(准确说s是文件的路径)
bool SPIFFS.remove(String s); //删除文件s,返回是否删除成功
\n
1
2
3
4
5
6
File f = SPIFFS.open(file_name, "r");  //以读的方式打开一个文件

//打印文件中所有内容
for(int i=0; i<f.size(); i++){
Serial.print((char)f.read());
}
\n
1
2
3
4
//以添加信息方式打开一个文件
File f = SPIFFS.open(file_name, "a");
f.println("This is Appended Info."); // 添加字符串信息
f.close(); //关闭文件
\n
\n

注意:不论以何种方式打开文件,最后都要记得关闭文件

\n
\n

基于目录的操作

获取一个目录对象 openDir()

\n
1
2
3
4
5
6
7
String folder_name = "/folder";    //被读取的文件夹
Dir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象

// dir.next()可以看作一个指针,每循环一次就会指向下一个元素
while (dir.next()) {
Serial.println(dir.fileName()); // 输出文件名
}
\n

闪存文件系统的基本信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FSInfo fs_info;    // 创建一个基本信息的对象

// 写入闪存文件系统信息到fs_info
SPIFFS.info(fs_info);

Serial.print(fs_info.totalBytes); // 可用空间总和(Bytes)
Serial.print(fs_info.usedBytes); // 已经用掉的空间(Bytes)

// 最大文件名字符限制(含路径和'\\0')
Serial.println(fs_info.maxPathLength);

// 最多允许打开文件数量
Serial.println(fs_info.maxOpenFiles);

// 存储块大小
Serial.println(fs_info.blockSize);

// 存储页大小
Serial.println(fs_info.pageSize);
\n

2、通过Arduino IDE向闪存文件系统上传文件

看太极创客这篇文章

\n
【注意】
    \n
  1. 上传文件前,把【串口监视器】关闭
  2. \n
  3. 之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了
  4. \n
\n

3、使用闪存系统配置功能更丰富的网络服务器

太极创客文章

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

1、文件基本操作

ESP8266配有一个闪存,这就像是一个小硬盘,上传的文件就可以存放在这个闪存里,这个闪存简称为 SPIFFS

\n

代码中需要用到头文件<FS.h> 包含的类SPIFFS

\n

下面教你如何打开一个文件并向里面写如信息

\n
    \n
  1. 格式化SPIFFS

    \n
    1
    SPIFFS.format(); 
    \n
  2. \n
  3. 启动SPIFFS

    \n
    1
    SPIFFS.begin();   //该函数会返回一个bool型结果,启动成功返回true,否则为false
    \n
  4. \n
  5. 用open函数打开一个文件,如果不存在就会创建文件(打开文件->写入数据->关闭文件)

    \n
    1
    2
    3
    4
    5
    //以写入的方式打开一个文件,filename为文件路径
    String file_name = "/folder/notes.txt"
    File dataFile = SPIFFS.open(file_name, "w");\t\t
    dataFile.println("Hello World"); // 向dataFile写入字符串信息
    dataFile.close(); // 关闭文件
    \n
  6. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <FS.h>

void setup(){
Serial.begin(9600);

String filename = "/folder/notes.txt";
SPIFFS.format();
if(SPIFFS.begin()) Serial.println("SPIFFS Started...");
else Serial.println("SPIFFS Failed to Start...");

File f = SPIFFS.open(filename, "w");
f.println("Hello esp8266 world");
f.close();
Serial.println("Finish writing the file...");

}

void loop(){

}
\n

SPIFFS基于文件的基本操作

1
2
3
4
void SPIFFS.format();   //格式化闪存文件系统 【注意:格式化文件系统需要耗费一定时间】

bool SPIFFS.exists(String s); //是否存在文件名为s的文件(准确说s是文件的路径)
bool SPIFFS.remove(String s); //删除文件s,返回是否删除成功
\n
1
2
3
4
5
6
File f = SPIFFS.open(file_name, "r");  //以读的方式打开一个文件

//打印文件中所有内容
for(int i=0; i<f.size(); i++){
Serial.print((char)f.read());
}
\n
1
2
3
4
//以添加信息方式打开一个文件
File f = SPIFFS.open(file_name, "a");
f.println("This is Appended Info."); // 添加字符串信息
f.close(); //关闭文件
\n
\n

注意:不论以何种方式打开文件,最后都要记得关闭文件

\n
\n

基于目录的操作

获取一个目录对象 openDir()

\n
1
2
3
4
5
6
7
String folder_name = "/folder";    //被读取的文件夹
Dir dir = SPIFFS.openDir(folder_name); // 建立“目录”对象

// dir.next()可以看作一个指针,每循环一次就会指向下一个元素
while (dir.next()) {
Serial.println(dir.fileName()); // 输出文件名
}
\n

闪存文件系统的基本信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FSInfo fs_info;    // 创建一个基本信息的对象

// 写入闪存文件系统信息到fs_info
SPIFFS.info(fs_info);

Serial.print(fs_info.totalBytes); // 可用空间总和(Bytes)
Serial.print(fs_info.usedBytes); // 已经用掉的空间(Bytes)

// 最大文件名字符限制(含路径和'\\0')
Serial.println(fs_info.maxPathLength);

// 最多允许打开文件数量
Serial.println(fs_info.maxOpenFiles);

// 存储块大小
Serial.println(fs_info.blockSize);

// 存储页大小
Serial.println(fs_info.pageSize);
\n

2、通过Arduino IDE向闪存文件系统上传文件

看太极创客这篇文章

\n
【注意】
    \n
  1. 上传文件前,把【串口监视器】关闭
  2. \n
  3. 之前上传的代码中没有使用格式化,否则刚上传的文件就被格式化删除了
  4. \n
\n

3、使用闪存系统配置功能更丰富的网络服务器

太极创客文章

\n"},{"title":"MQTT篇(一)MQTT的认识及原理和操作","abbrlink":"73ba8569","date":"2021-08-09T02:21:00.000Z","description":"MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n\n\n## 什么是MQTT\n\n\n\nMQTT是一个客户端服务端架构的发布/订阅模式的**消息传输协议**。\n\n优点:\n\n轻巧、开放、简单、规范,易于实现。\n\n这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。\n\n\n\n\n\n## 基本原理\n\n\n\n在MQTT协议通讯中,有两个最为重要的角色。它们分别是**服务端**和**客户端**。\n\n**服务端**:\n\n是消息传输的枢纽\n\n**客户端**:\n\n客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。\n\n客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。\n\n![image-20210810220807339](https://gitee.com/ajream/images/raw/master/img/2021-08-1022-08-14_image-20210810220807339.png)\n\n\n\n\n\n\n\n## 客户端连接服务端\n\n分2个步骤\n\n1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息\n2. 服务端收到请求后,发送数据包“CONNACK”进行确认\n\n\n\n### CONNECT报文\n\n这是该报文的信息:\n\n![MQTT CONNECT 报文内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-connect.gif)\n\n\n\n**clientID**\n\nclientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端\n\n\n\n**cleanSession – 清除会话**\n\n表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;\n\n取值true表示保存,false表示不保存\n\n> 注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。\n>\n> 反之如果数据不会重要,可以取值为false\n\n\n\n**keepAlive** —— 间隔时间\n\n用于服务端每隔多久就了解一下客户端是否与其保持连接的情况\n\n\n\n### CONNACK – 确认连接请求\n\n\n\n![MQTT CONNACK 信息内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/connack.gif)\n\n\n\n**sessionPresent**\n\nCONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。\n\ncleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。\n\n总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。\n\n\n\n**returnCode** —— 返回码\n\n当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况\n\n| 返回码 | 返回码描述 |\n| :----: | :----------------------------------------------------------- |\n| 0 | 成功连接 |\n| 1 | 连接被服务端拒绝,原因是不支持客户端的MQTT协议版本 |\n| 2 | 连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。 |\n| 3 | 连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。 |\n| 4 | 连接被服务端拒绝,原因是用户名或密码无效。 |\n| 5 | 连接被服务端拒绝,原因是客户端未被授权连接到此服务端。 |\n\n\n\n\n\n## ESP8266连接MQTT服务端\n\nhttp://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883\n\n接下来使用`PubSubClient`库(去[GitHub](https://github.com/knolleary/pubsubclient/)下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。\n\n1. 配置WiFi\n\n2. 创建mqtt客户端对象(间接创建)\n\n ```c\n WiFiClient wifiClient;\n PubSubClient mqttClient(wifiClient);\n ```\n\n3. 配置客户端要连接哪个服务端(绑定服务端)\n\n ```c\n String mqttServer = \"test.ranye-iot.net\";\n const int port = 1883;\n mqttClient.setServer(mqttServer, port); //连接服务端\n ```\n\n4. 连接服务端\n\n ```c\n //用设备的mac地址来生成唯一标识该设备的clientID\n String clientID = \"esp8266-\" + WiFi.macAddress(); \n \n mqttClient.connect(clientId.c_str()) //连接成功返回true\n ```\n\n \n\n5. 连接成功后保持 “心跳”,否则...(可以尝试继续连接)\n\n ```c\n void loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"rbook\";\nconst char* password = \"12345678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n \n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n \nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n \nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n}\n \nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n \nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","source":"_posts/esp8266/MQTT篇(一)-认识MQTT与基本操作.md","raw":"---\ntitle: MQTT篇(一)MQTT的认识及原理和操作\ntags:\n - 物联网\n - esp8266\ncategories:\n - 硬件学习\n - 物联网开发\nabbrlink: 73ba8569\ndate: 2021-08-09 10:21:00\ndescription: MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n\n\n## 什么是MQTT\n\n\n\nMQTT是一个客户端服务端架构的发布/订阅模式的**消息传输协议**。\n\n优点:\n\n轻巧、开放、简单、规范,易于实现。\n\n这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。\n\n\n\n\n\n## 基本原理\n\n\n\n在MQTT协议通讯中,有两个最为重要的角色。它们分别是**服务端**和**客户端**。\n\n**服务端**:\n\n是消息传输的枢纽\n\n**客户端**:\n\n客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。\n\n客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。\n\n![image-20210810220807339](https://gitee.com/ajream/images/raw/master/img/2021-08-1022-08-14_image-20210810220807339.png)\n\n\n\n\n\n\n\n## 客户端连接服务端\n\n分2个步骤\n\n1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息\n2. 服务端收到请求后,发送数据包“CONNACK”进行确认\n\n\n\n### CONNECT报文\n\n这是该报文的信息:\n\n![MQTT CONNECT 报文内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-connect.gif)\n\n\n\n**clientID**\n\nclientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端\n\n\n\n**cleanSession – 清除会话**\n\n表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;\n\n取值true表示保存,false表示不保存\n\n> 注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。\n>\n> 反之如果数据不会重要,可以取值为false\n\n\n\n**keepAlive** —— 间隔时间\n\n用于服务端每隔多久就了解一下客户端是否与其保持连接的情况\n\n\n\n### CONNACK – 确认连接请求\n\n\n\n![MQTT CONNACK 信息内容](http://www.taichi-maker.com/wp-content/uploads/2020/09/connack.gif)\n\n\n\n**sessionPresent**\n\nCONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。\n\ncleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。\n\n总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。\n\n\n\n**returnCode** —— 返回码\n\n当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况\n\n| 返回码 | 返回码描述 |\n| :----: | :----------------------------------------------------------- |\n| 0 | 成功连接 |\n| 1 | 连接被服务端拒绝,原因是不支持客户端的MQTT协议版本 |\n| 2 | 连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。 |\n| 3 | 连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。 |\n| 4 | 连接被服务端拒绝,原因是用户名或密码无效。 |\n| 5 | 连接被服务端拒绝,原因是客户端未被授权连接到此服务端。 |\n\n\n\n\n\n## ESP8266连接MQTT服务端\n\nhttp://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883\n\n接下来使用`PubSubClient`库(去[GitHub](https://github.com/knolleary/pubsubclient/)下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。\n\n1. 配置WiFi\n\n2. 创建mqtt客户端对象(间接创建)\n\n ```c\n WiFiClient wifiClient;\n PubSubClient mqttClient(wifiClient);\n ```\n\n3. 配置客户端要连接哪个服务端(绑定服务端)\n\n ```c\n String mqttServer = \"test.ranye-iot.net\";\n const int port = 1883;\n mqttClient.setServer(mqttServer, port); //连接服务端\n ```\n\n4. 连接服务端\n\n ```c\n //用设备的mac地址来生成唯一标识该设备的clientID\n String clientID = \"esp8266-\" + WiFi.macAddress(); \n \n mqttClient.connect(clientId.c_str()) //连接成功返回true\n ```\n\n \n\n5. 连接成功后保持 “心跳”,否则...(可以尝试继续连接)\n\n ```c\n void loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n }\n ```\n\n\n\n完整code:\n\n```c\n#include \n#include \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"rbook\";\nconst char* password = \"12345678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n \n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n \nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n \nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n}\n \nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器 \n mqttClient.loop(); // 保持客户端心跳\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n \nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","slug":"esp8266/MQTT篇(一)-认识MQTT与基本操作","published":1,"updated":"2021-09-11T07:53:28.088Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6px0047akve8seydg2u","content":"

什么是MQTT

MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议

\n

优点:

\n

轻巧、开放、简单、规范,易于实现。

\n

这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。

\n

基本原理

在MQTT协议通讯中,有两个最为重要的角色。它们分别是服务端客户端

\n

服务端

\n

是消息传输的枢纽

\n

客户端

\n

客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。

\n

客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。

\n

\"image-20210810220807339\"

\n

客户端连接服务端

分2个步骤

\n
    \n
  1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息
  2. \n
  3. 服务端收到请求后,发送数据包“CONNACK”进行确认
  4. \n
\n

CONNECT报文

这是该报文的信息:

\n

\"MQTT

\n

clientID

\n

clientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端

\n

cleanSession – 清除会话

\n

表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;

\n

取值true表示保存,false表示不保存

\n
\n

注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。

\n

反之如果数据不会重要,可以取值为false

\n
\n

keepAlive —— 间隔时间

\n

用于服务端每隔多久就了解一下客户端是否与其保持连接的情况

\n

CONNACK – 确认连接请求

\"MQTT

\n

sessionPresent

\n

CONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。

\n

cleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。

\n

总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。

\n

returnCode —— 返回码

\n

当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
返回码返回码描述
0成功连接
1连接被服务端拒绝,原因是不支持客户端的MQTT协议版本
2连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。
3连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。
4连接被服务端拒绝,原因是用户名或密码无效。
5连接被服务端拒绝,原因是客户端未被授权连接到此服务端。
\n
\n

ESP8266连接MQTT服务端

http://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883

\n

接下来使用PubSubClient库(去GitHub下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。

\n
    \n
  1. 配置WiFi

    \n
  2. \n
  3. 创建mqtt客户端对象(间接创建)

    \n
    1
    2
    WiFiClient wifiClient;
    PubSubClient mqttClient(wifiClient);
    \n
  4. \n
  5. 配置客户端要连接哪个服务端(绑定服务端)

    \n
    1
    2
    3
    String mqttServer = "test.ranye-iot.net";
    const int port = 1883;
    mqttClient.setServer(mqttServer, port); //连接服务端
    \n
  6. \n
  7. 连接服务端

    \n
    1
    2
    3
    4
    //用设备的mac地址来生成唯一标识该设备的clientID
    String clientID = "esp8266-" + WiFi.macAddress();

    mqttClient.connect(clientId.c_str()) //连接成功返回true
    \n
  8. \n
\n
    \n
  1. 连接成功后保持 “心跳”,否则…(可以尝试继续连接)

    \n
    1
    2
    3
    4
    5
    6
    7
    void loop() { 
    if (mqttClient.connected()) { // 如果开发板成功连接服务器
    mqttClient.loop(); // 保持客户端心跳
    } else { // 如果开发板未能成功连接服务器
    connectMQTTServer(); // 则尝试连接服务器
    }
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "rbook";
const char* password = "12345678";
const char* mqttServer = "test.ranye-iot.net";

// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void setup() {
Serial.begin(9600);

//设置ESP8266工作模式为无线终端模式
WiFi.mode(WIFI_STA);

// 连接WiFi
connectWifi();

// 设置MQTT服务器和端口号
mqttClient.setServer(mqttServer, 1883);

// 连接MQTT服务器
connectMQTTServer();
}

void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
mqttClient.loop(); // 保持客户端心跳
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}

void connectMQTTServer(){
// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
String clientId = "esp8266-" + WiFi.macAddress();

// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}

// ESP8266连接wifi
void connectWifi(){

WiFi.begin(ssid, password);

//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

什么是MQTT

MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议

\n

优点:

\n

轻巧、开放、简单、规范,易于实现。

\n

这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。

\n

基本原理

在MQTT协议通讯中,有两个最为重要的角色。它们分别是服务端客户端

\n

服务端

\n

是消息传输的枢纽

\n

客户端

\n

客户端有多个,它们之间不能直接相互通信,中间使用服务端来处理消息分配。

\n

客户端要获取某个主题信息,要向服务端进行主题 “订阅”,订阅后可以向服务端发送与主题相关的信息,其他客户端再从服务端获取信息。

\n

\"image-20210810220807339\"

\n

客户端连接服务端

分2个步骤

\n
    \n
  1. 客户端向服务端发送请求数据包“CONNECT”(也叫报文),其中包含了连接请求的信息
  2. \n
  3. 服务端收到请求后,发送数据包“CONNACK”进行确认
  4. \n
\n

CONNECT报文

这是该报文的信息:

\n

\"MQTT

\n

clientID

\n

clientId是MQTT客户端的唯一标识,MQTT服务端以此来识别客户端

\n

cleanSession – 清除会话

\n

表示如果客户端未能正确接收到数据时,服务端是否要对数据进行保存;

\n

取值true表示保存,false表示不保存

\n
\n

注意:如果数据很重要,客户端没有正确接收到会造成严重后果,建议取值true,让服务端先把会话保留,待客户端能正常接收再发送。

\n

反之如果数据不会重要,可以取值为false

\n
\n

keepAlive —— 间隔时间

\n

用于服务端每隔多久就了解一下客户端是否与其保持连接的情况

\n

CONNACK – 确认连接请求

\"MQTT

\n

sessionPresent

\n

CONNACK报文的sessionPresent与CONNECT报文的cleanSession相互配合。

\n

cleanSession=true时,sessionPresent应该配置为false,即不需要保存信息;反之sessionPresent应该配置为true,即需要保存会话信息。

\n

总之,sessionPresent作用是客户端发送连接请求时,服务端告知客户端有没有保存报文信息。这个被服务端保存的报文信息是来自于上一次客户端连接时,服务端曾经发送此报文给客户端,但是发送后没有收到客户端接收确认。

\n

returnCode —— 返回码

\n

当服务端收到了客户端的连接请求后,会向客户端发送returnCode(返回码),用以说明连接情况

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
返回码返回码描述
0成功连接
1连接被服务端拒绝,原因是不支持客户端的MQTT协议版本
2连接被服务端拒绝,原因是不支持客户端标识符的编码。 可能造成此原因的是客户端标识符编码是UTF-8,但是服务端不允许使用此编码。
3连接被服务端拒绝,原因是服务端不可用。 即,网络连接已经建立,但MQTT服务不可用。
4连接被服务端拒绝,原因是用户名或密码无效。
5连接被服务端拒绝,原因是客户端未被授权连接到此服务端。
\n
\n

ESP8266连接MQTT服务端

http://test.ranye-iot.net 是国内一个MQTT服务器平台地址,TCP端口为1883

\n

接下来使用PubSubClient库(去GitHub下载zip包后通过ArduinoIDE添加即可)来实现MQTT物联网应用。

\n
    \n
  1. 配置WiFi

    \n
  2. \n
  3. 创建mqtt客户端对象(间接创建)

    \n
    1
    2
    WiFiClient wifiClient;
    PubSubClient mqttClient(wifiClient);
    \n
  4. \n
  5. 配置客户端要连接哪个服务端(绑定服务端)

    \n
    1
    2
    3
    String mqttServer = "test.ranye-iot.net";
    const int port = 1883;
    mqttClient.setServer(mqttServer, port); //连接服务端
    \n
  6. \n
  7. 连接服务端

    \n
    1
    2
    3
    4
    //用设备的mac地址来生成唯一标识该设备的clientID
    String clientID = "esp8266-" + WiFi.macAddress();

    mqttClient.connect(clientId.c_str()) //连接成功返回true
    \n
  8. \n
\n
    \n
  1. 连接成功后保持 “心跳”,否则…(可以尝试继续连接)

    \n
    1
    2
    3
    4
    5
    6
    7
    void loop() { 
    if (mqttClient.connected()) { // 如果开发板成功连接服务器
    mqttClient.loop(); // 保持客户端心跳
    } else { // 如果开发板未能成功连接服务器
    connectMQTTServer(); // 则尝试连接服务器
    }
    }
    \n
  2. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "rbook";
const char* password = "12345678";
const char* mqttServer = "test.ranye-iot.net";

// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void setup() {
Serial.begin(9600);

//设置ESP8266工作模式为无线终端模式
WiFi.mode(WIFI_STA);

// 连接WiFi
connectWifi();

// 设置MQTT服务器和端口号
mqttClient.setServer(mqttServer, 1883);

// 连接MQTT服务器
connectMQTTServer();
}

void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
mqttClient.loop(); // 保持客户端心跳
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}

void connectMQTTServer(){
// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
String clientId = "esp8266-" + WiFi.macAddress();

// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}

// ESP8266连接wifi
void connectWifi(){

WiFi.begin(ssid, password);

//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}
\n"},{"title":"esp8266(8)-OTA上传代码","abbrlink":"1b6a7930","date":"2021-08-05T05:00:00.000Z","description":"esp8266如何实现无线上传代码","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。\n\n\n\n在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。\n\n## 1、通过数据线上传初始示例程序\n\n首先,请将以下示例程序通过Arduino IDE上传到ESP8266。\n\n```c\n#include \n#include \n#include \n \n// 闪烁时间间隔(秒)\nconst int blinkInterval = 2; \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"taichimaker\";\nconst char* password = \"12345678\";\n \nTicker ticker;\n \nvoid setup() {\n Serial.begin(9600); \n Serial.println(\"\");\n pinMode(LED_BUILTIN, OUTPUT);\n \n ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象\n \n connectWifi();\n \n // OTA设置并启动\n ArduinoOTA.setHostname(\"ESP8266\");\n ArduinoOTA.setPassword(\"12345678\");\n ArduinoOTA.begin();\n \n Serial.println(\"OTA ready\");\n}\nvoid loop() {\n ArduinoOTA.handle();\n}\n \n// 在Tinker对象控制下,此函数将会定时执行。\nvoid tickerCount(){\n digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));\n}\n \nvoid connectWifi(){\n //开始连接wifi\n WiFi.begin(ssid, password);\n \n //等待WiFi连接,连接成功打印IP\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.print(\"IP address:\\t\"); \n Serial.println(WiFi.localIP()); \n}\n```\n\n\n\n## 2、通过Arduino IDE正确选择OTA端口\n\n程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。\n\n如下图所示:\n\n[![esp8266 OTA 端口选择](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port-1024x547.jpg)](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port.jpg)\n\n\n\n## 3、认证并上传程序\n\n\n\n点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的`setPassword`函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。\n\n程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。\n\n\n\n## 4、OTA的局限性\n\n\n\n**1. 程序占用空间变大**\n在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。\n\n**2. Arduino IDE无法通过OTA端口与开发板进行串口通讯**\n当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。\n\n**3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi**\n若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。\n\n","source":"_posts/esp8266/8-OTA上传代码.md","raw":"---\ntitle: esp8266(8)-OTA上传代码\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: 1b6a7930\ndate: 2021-08-05 13:00:00\ndescription: esp8266如何实现无线上传代码\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。\n\n\n\n在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。\n\n## 1、通过数据线上传初始示例程序\n\n首先,请将以下示例程序通过Arduino IDE上传到ESP8266。\n\n```c\n#include \n#include \n#include \n \n// 闪烁时间间隔(秒)\nconst int blinkInterval = 2; \n \n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"taichimaker\";\nconst char* password = \"12345678\";\n \nTicker ticker;\n \nvoid setup() {\n Serial.begin(9600); \n Serial.println(\"\");\n pinMode(LED_BUILTIN, OUTPUT);\n \n ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象\n \n connectWifi();\n \n // OTA设置并启动\n ArduinoOTA.setHostname(\"ESP8266\");\n ArduinoOTA.setPassword(\"12345678\");\n ArduinoOTA.begin();\n \n Serial.println(\"OTA ready\");\n}\nvoid loop() {\n ArduinoOTA.handle();\n}\n \n// 在Tinker对象控制下,此函数将会定时执行。\nvoid tickerCount(){\n digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));\n}\n \nvoid connectWifi(){\n //开始连接wifi\n WiFi.begin(ssid, password);\n \n //等待WiFi连接,连接成功打印IP\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.print(\"IP address:\\t\"); \n Serial.println(WiFi.localIP()); \n}\n```\n\n\n\n## 2、通过Arduino IDE正确选择OTA端口\n\n程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。\n\n如下图所示:\n\n[![esp8266 OTA 端口选择](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port-1024x547.jpg)](http://www.taichi-maker.com/wp-content/uploads/2020/07/esp8266_select_port.jpg)\n\n\n\n## 3、认证并上传程序\n\n\n\n点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的`setPassword`函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。\n\n程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。\n\n\n\n## 4、OTA的局限性\n\n\n\n**1. 程序占用空间变大**\n在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。\n\n**2. Arduino IDE无法通过OTA端口与开发板进行串口通讯**\n当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。\n\n**3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi**\n若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。\n\n","slug":"esp8266/8-OTA上传代码","published":1,"updated":"2021-09-11T07:55:01.650Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6py0049akve15r5eo74","content":"

所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。

\n

在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。

\n

1、通过数据线上传初始示例程序

首先,请将以下示例程序通过Arduino IDE上传到ESP8266。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <Ticker.h>

// 闪烁时间间隔(秒)
const int blinkInterval = 2;

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "taichimaker";
const char* password = "12345678";

Ticker ticker;

void setup() {
Serial.begin(9600);
Serial.println("");
pinMode(LED_BUILTIN, OUTPUT);

ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象

connectWifi();

// OTA设置并启动
ArduinoOTA.setHostname("ESP8266");
ArduinoOTA.setPassword("12345678");
ArduinoOTA.begin();

Serial.println("OTA ready");
}
void loop() {
ArduinoOTA.handle();
}

// 在Tinker对象控制下,此函数将会定时执行。
void tickerCount(){
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}

void connectWifi(){
//开始连接wifi
WiFi.begin(ssid, password);

//等待WiFi连接,连接成功打印IP
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.print("IP address:\\t");
Serial.println(WiFi.localIP());
}
\n

2、通过Arduino IDE正确选择OTA端口

程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。

\n

如下图所示:

\n

\"esp8266

\n

3、认证并上传程序

点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的setPassword函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。

\n

程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。

\n

4、OTA的局限性

1. 程序占用空间变大
在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。

\n

2. Arduino IDE无法通过OTA端口与开发板进行串口通讯
当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。

\n

3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi
若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

所谓OTA,就是Over-The-Air的缩写,通过OTA我们无需将ESP8266与电脑连接,而仅仅通过WiFi就可以用Arduino IDE向ESP8266上传程序。

\n

在这一节课程里,我们将分步骤向您介绍如何实现ESP8266的OTA操作。

\n

1、通过数据线上传初始示例程序

首先,请将以下示例程序通过Arduino IDE上传到ESP8266。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <Ticker.h>

// 闪烁时间间隔(秒)
const int blinkInterval = 2;

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "taichimaker";
const char* password = "12345678";

Ticker ticker;

void setup() {
Serial.begin(9600);
Serial.println("");
pinMode(LED_BUILTIN, OUTPUT);

ticker.attach(blinkInterval, tickerCount); // 设置Ticker对象

connectWifi();

// OTA设置并启动
ArduinoOTA.setHostname("ESP8266");
ArduinoOTA.setPassword("12345678");
ArduinoOTA.begin();

Serial.println("OTA ready");
}
void loop() {
ArduinoOTA.handle();
}

// 在Tinker对象控制下,此函数将会定时执行。
void tickerCount(){
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}

void connectWifi(){
//开始连接wifi
WiFi.begin(ssid, password);

//等待WiFi连接,连接成功打印IP
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.print("IP address:\\t");
Serial.println(WiFi.localIP());
}
\n

2、通过Arduino IDE正确选择OTA端口

程序上传后,请重新启动Arduino IDE。并且通过Arduino IDE正确选择ESP8266的OTA端口。

\n

如下图所示:

\n

\"esp8266

\n

3、认证并上传程序

点击Arduino IDE的”上传”按钮后, IDE将会弹出对话框让用户输入OTA上传密码。请根据示例程序中的setPassword函数所设置的信息来输入密码。完成密码输入后,点击确定。如果密码无误,您将看到程序开始上传。

\n

程序上传结束后,ESP8266将会自动重启开发板,新的程序也将在重启后开始运行。

\n

4、OTA的局限性

1. 程序占用空间变大
在OTA上传新程序过程中, ESP8266开发板将会保持旧程序的运行。这将导致ESP8266开发板的程序占用空间翻倍。假如您的程序非常复杂,占用空间很大,那么使用OTA上传就不太适合了。

\n

2. Arduino IDE无法通过OTA端口与开发板进行串口通讯
当Arduino IDE的上传端口选为“网络端口”,Arduino IDE将无法获取ESP8266的串口通讯数据。不过ESP8266的串口通讯并不会因为OTA功能而受到影响。换句话说,您可以使用其它电脑串口通讯软件,如Putty等,来实现ESP8266与电脑之间的串口通讯。

\n

3.使用OTA上传程序的电脑与ESP8266必须连接同一WiFi
若要使用OTA上传功能,那么电脑和ESP8266必须要在同一WiFi中,否则是无法实现OTA上传的。

\n"},{"title":"esp8266(7)-多任务执行","abbrlink":"c25015c5","date":"2021-08-04T01:00:00.000Z","description":"esp8266如何同时执行多个任务","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n通过Ticker库可以让esp8266同时执行多个任务\n\n\n\n## 1、如何实现\n\n示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息\n\n1. 创建 `Ticker`对象\n\n ```c\n Ticker ticker;\n ```\n\n2. 定时执行某个函数\n\n ```c\n ticker.attach(time, func); //time单位为s,func为函数名\n ticker.attach_ms(time, func); //time单位为ms,func为函数名\n ```\n\n \n\n```c\n#include \n\nTicker ticker;// 建立Ticker用于实现定时功能\nint count; // 计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n pinMode(LED_BUILTIN, OUTPUT);\n\n // attach函数的第一个参数是控制定时间隔的变量。该参数的单位为秒。第二个参数是\n // 定时执行的函数名称。\n ticker.attach(1, sayHi);\n}\n\nvoid loop() {\n // 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务\n for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n\n for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n delay(3000);\n}\n\n// 在Tinker对象控制下,此函数将会定时执行。\nvoid sayHi(){\n count++;\n Serial.print(\"Hi \");\n Serial.println(count);\n}\n```\n\n\n\n## 2、其他操作\n\n\n\n- 停止执行定时任务:ticker.detach()\n\n- 向定时调用函数传递参数:ticker.attach(1, sayHi, 8)\n\n 【注意】:\n\n attach函数所能传递的参数最多只有一个\n\n 该参数仅能是以下类型中的一种:char, short, int, float, void\\*, char\\*\n\n- **利用多个Ticker对象可以让ESP8266处理多任务**\n\n\n\n","source":"_posts/esp8266/7-多任务执行.md","raw":"---\ntitle: esp8266(7)-多任务执行\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: c25015c5\ndate: 2021-08-04 09:00:00\ndescription: esp8266如何同时执行多个任务\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n通过Ticker库可以让esp8266同时执行多个任务\n\n\n\n## 1、如何实现\n\n示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息\n\n1. 创建 `Ticker`对象\n\n ```c\n Ticker ticker;\n ```\n\n2. 定时执行某个函数\n\n ```c\n ticker.attach(time, func); //time单位为s,func为函数名\n ticker.attach_ms(time, func); //time单位为ms,func为函数名\n ```\n\n \n\n```c\n#include \n\nTicker ticker;// 建立Ticker用于实现定时功能\nint count; // 计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n pinMode(LED_BUILTIN, OUTPUT);\n\n // attach函数的第一个参数是控制定时间隔的变量。该参数的单位为秒。第二个参数是\n // 定时执行的函数名称。\n ticker.attach(1, sayHi);\n}\n\nvoid loop() {\n // 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务\n for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n\n for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {\n analogWrite(LED_BUILTIN, fadeValue);\n delay(10);\n }\n delay(3000);\n}\n\n// 在Tinker对象控制下,此函数将会定时执行。\nvoid sayHi(){\n count++;\n Serial.print(\"Hi \");\n Serial.println(count);\n}\n```\n\n\n\n## 2、其他操作\n\n\n\n- 停止执行定时任务:ticker.detach()\n\n- 向定时调用函数传递参数:ticker.attach(1, sayHi, 8)\n\n 【注意】:\n\n attach函数所能传递的参数最多只有一个\n\n 该参数仅能是以下类型中的一种:char, short, int, float, void\\*, char\\*\n\n- **利用多个Ticker对象可以让ESP8266处理多任务**\n\n\n\n","slug":"esp8266/7-多任务执行","published":1,"updated":"2021-09-11T07:54:50.150Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6py004cakve0g6zci7t","content":"

通过Ticker库可以让esp8266同时执行多个任务

\n

1、如何实现

示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息

\n
    \n
  1. 创建 Ticker对象

    \n
    1
    Ticker ticker;
    \n
  2. \n
  3. 定时执行某个函数

    \n
    1
    2
    ticker.attach(time, func);   //time单位为s,func为函数名
    ticker.attach_ms(time, func); //time单位为ms,func为函数名
    \n
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <Ticker.h>

Ticker ticker;// 建立Ticker用于实现定时功能
int count; // 计数用变量

void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);

// attach函数的第一个参数是控制定时间隔的变量。该参数的单位为秒。第二个参数是
// 定时执行的函数名称。
ticker.attach(1, sayHi);
}

void loop() {
// 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务
for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {
analogWrite(LED_BUILTIN, fadeValue);
delay(10);
}

for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {
analogWrite(LED_BUILTIN, fadeValue);
delay(10);
}
delay(3000);
}

// 在Tinker对象控制下,此函数将会定时执行。
void sayHi(){
count++;
Serial.print("Hi ");
Serial.println(count);
}
\n

2、其他操作

    \n
  • 停止执行定时任务:ticker.detach()

    \n
  • \n
  • 向定时调用函数传递参数:ticker.attach(1, sayHi, 8)

    \n

    【注意】:

    \n

    attach函数所能传递的参数最多只有一个

    \n

    该参数仅能是以下类型中的一种:char, short, int, float, void*, char*

    \n
  • \n
  • 利用多个Ticker对象可以让ESP8266处理多任务

    \n
  • \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

通过Ticker库可以让esp8266同时执行多个任务

\n

1、如何实现

示例:在实现一个呼吸灯的同时,在串口监视器打印一些字符信息

\n
    \n
  1. 创建 Ticker对象

    \n
    1
    Ticker ticker;
    \n
  2. \n
  3. 定时执行某个函数

    \n
    1
    2
    ticker.attach(time, func);   //time单位为s,func为函数名
    ticker.attach_ms(time, func); //time单位为ms,func为函数名
    \n
  4. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <Ticker.h>

Ticker ticker;// 建立Ticker用于实现定时功能
int count; // 计数用变量

void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);

// attach函数的第一个参数是控制定时间隔的变量。该参数的单位为秒。第二个参数是
// 定时执行的函数名称。
ticker.attach(1, sayHi);
}

void loop() {
// 用LED呼吸灯效果来演示在Tinker对象控制下,ESP8266可以定时执行其它任务
for (int fadeValue = 0 ; fadeValue <= 1023; fadeValue += 5) {
analogWrite(LED_BUILTIN, fadeValue);
delay(10);
}

for (int fadeValue = 1023 ; fadeValue >= 0; fadeValue -= 5) {
analogWrite(LED_BUILTIN, fadeValue);
delay(10);
}
delay(3000);
}

// 在Tinker对象控制下,此函数将会定时执行。
void sayHi(){
count++;
Serial.print("Hi ");
Serial.println(count);
}
\n

2、其他操作

    \n
  • 停止执行定时任务:ticker.detach()

    \n
  • \n
  • 向定时调用函数传递参数:ticker.attach(1, sayHi, 8)

    \n

    【注意】:

    \n

    attach函数所能传递的参数最多只有一个

    \n

    该参数仅能是以下类型中的一种:char, short, int, float, void*, char*

    \n
  • \n
  • 利用多个Ticker对象可以让ESP8266处理多任务

    \n
  • \n
\n"},{"title":"esp8266(6)-WiFi配网","abbrlink":"baad5185","date":"2021-08-04T00:00:00.000Z","description":"使用手机等终端设备辅助esp8266进行联网","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n\n\n之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?\n\n这里主要用到 `WiFiManager`库,可以去太极创客[下载](https://github.com/taichi-maker/WiFiManager/archive/refs/heads/master.zip)并加载到ArduinoIDE中。\n\n\n\n## 1、清除WiFi信息\n\n使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了\n\n只需2个步骤:\n\n1. 创建WiFiManager对象\n\n2. 清除WiFi信息\n\n ```c\n WiFiManager wifiManager;\t\t//创建对象\n wifiManager.resetSettings();\t//清除WiFi信息\n ```\n\n完整code\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果\n wifiManager.resetSettings();\n Serial.println(\"ESP8266 WiFi Settings Cleared\");\n}\n \nvoid loop() {}\n```\n\n\n\n\n\n## 2、WiFi配网\n\n1. 创建WiFiManager对象\n\n2. 开启接入点模式(就是在开发板上开个热点,让周围设备连接)\n\n ```c\n wifiManager.autoConnect(\"热点名称\"[, \"密码\"]);\n ```\n\n3. 接下来就可以用手机、电脑配网\n\n (1)用手机(或电脑)连接开发板的热点\n\n (2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi\n\n (3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网\n\n (4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要[擦除已连接的WiFi信息](#1、清除WiFi信息)\n\n (5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。\n\n4. 判断是否配网成功\n\n 可以在串口监视屏打印连接上的WiFi名称\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称\n wifiManager.autoConnect(\"ESP8266-wifi\");\n \n // 如果您希望该WiFi添加密码,可以使用以下语句:\n // wifiManager.autoConnect(\"ESP8266-wifi\", \"12345678\");\n \n \n // WiFi连接成功后将通过串口监视器输出连接成功信息 \n Serial.println(\"\"); \n Serial.print(\"ESP8266 Connected to \");\n Serial.println(WiFi.SSID()); // 连接上的WiFi名称\n Serial.print(\"IP address:\\t\");\n Serial.println(WiFi.localIP()); // IP\n}\n \nvoid loop() {}\n```\n\n![image-20210811095227391](https://gitee.com/ajream/images/raw/master/img/2021-08-1109-56-45_image-20210811095227391.png)\n\n","source":"_posts/esp8266/6-WiFi配网.md","raw":"---\ntitle: esp8266(6)-WiFi配网\ntags:\n - esp8266\ncategories: \n - 硬件学习\n - esp8266\nabbrlink: baad5185\ndate: 2021-08-04 08:00:00\ndescription: 使用手机等终端设备辅助esp8266进行联网\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n\n\n之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?\n\n这里主要用到 `WiFiManager`库,可以去太极创客[下载](https://github.com/taichi-maker/WiFiManager/archive/refs/heads/master.zip)并加载到ArduinoIDE中。\n\n\n\n## 1、清除WiFi信息\n\n使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了\n\n只需2个步骤:\n\n1. 创建WiFiManager对象\n\n2. 清除WiFi信息\n\n ```c\n WiFiManager wifiManager;\t\t//创建对象\n wifiManager.resetSettings();\t//清除WiFi信息\n ```\n\n完整code\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果\n wifiManager.resetSettings();\n Serial.println(\"ESP8266 WiFi Settings Cleared\");\n}\n \nvoid loop() {}\n```\n\n\n\n\n\n## 2、WiFi配网\n\n1. 创建WiFiManager对象\n\n2. 开启接入点模式(就是在开发板上开个热点,让周围设备连接)\n\n ```c\n wifiManager.autoConnect(\"热点名称\"[, \"密码\"]);\n ```\n\n3. 接下来就可以用手机、电脑配网\n\n (1)用手机(或电脑)连接开发板的热点\n\n (2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi\n\n (3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网\n\n (4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要[擦除已连接的WiFi信息](#1、清除WiFi信息)\n\n (5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。\n\n4. 判断是否配网成功\n\n 可以在串口监视屏打印连接上的WiFi名称\n\n\n\n完整code:\n\n```c\n#include \n#include \n#include \n#include \n \nvoid setup() {\n Serial.begin(9600); \n // 建立WiFiManager对象\n WiFiManager wifiManager;\n \n // 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称\n wifiManager.autoConnect(\"ESP8266-wifi\");\n \n // 如果您希望该WiFi添加密码,可以使用以下语句:\n // wifiManager.autoConnect(\"ESP8266-wifi\", \"12345678\");\n \n \n // WiFi连接成功后将通过串口监视器输出连接成功信息 \n Serial.println(\"\"); \n Serial.print(\"ESP8266 Connected to \");\n Serial.println(WiFi.SSID()); // 连接上的WiFi名称\n Serial.print(\"IP address:\\t\");\n Serial.println(WiFi.localIP()); // IP\n}\n \nvoid loop() {}\n```\n\n![image-20210811095227391](https://gitee.com/ajream/images/raw/master/img/2021-08-1109-56-45_image-20210811095227391.png)\n\n","slug":"esp8266/6-WiFi配网","published":1,"updated":"2021-09-11T07:54:46.084Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q0004eakve6dzd49z7","content":"

之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?

\n

这里主要用到 WiFiManager库,可以去太极创客下载并加载到ArduinoIDE中。

\n

1、清除WiFi信息

使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了

\n

只需2个步骤:

\n
    \n
  1. 创建WiFiManager对象

    \n
  2. \n
  3. 清除WiFi信息

    \n
    1
    2
    WiFiManager wifiManager;\t\t//创建对象
    wifiManager.resetSettings();\t//清除WiFi信息
    \n
  4. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <ESP8266WiFi.h>          
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

void setup() {
Serial.begin(9600);
// 建立WiFiManager对象
WiFiManager wifiManager;

// 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果
wifiManager.resetSettings();
Serial.println("ESP8266 WiFi Settings Cleared");
}

void loop() {}
\n

2、WiFi配网

    \n
  1. 创建WiFiManager对象

    \n
  2. \n
  3. 开启接入点模式(就是在开发板上开个热点,让周围设备连接)

    \n
    1
    wifiManager.autoConnect("热点名称"[, "密码"]);
    \n
  4. \n
  5. 接下来就可以用手机、电脑配网

    \n

    (1)用手机(或电脑)连接开发板的热点

    \n

    (2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi

    \n

    (3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网

    \n

    (4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要擦除已连接的WiFi信息

    \n

    (5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。

    \n
  6. \n
  7. 判断是否配网成功

    \n

    可以在串口监视屏打印连接上的WiFi名称

    \n
  8. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <ESP8266WiFi.h>          
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

void setup() {
Serial.begin(9600);
// 建立WiFiManager对象
WiFiManager wifiManager;

// 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称
wifiManager.autoConnect("ESP8266-wifi");

// 如果您希望该WiFi添加密码,可以使用以下语句:
// wifiManager.autoConnect("ESP8266-wifi", "12345678");


// WiFi连接成功后将通过串口监视器输出连接成功信息
Serial.println("");
Serial.print("ESP8266 Connected to ");
Serial.println(WiFi.SSID()); // 连接上的WiFi名称
Serial.print("IP address:\\t");
Serial.println(WiFi.localIP()); // IP
}

void loop() {}
\n

\"image-20210811095227391\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

之前nodeMCU一直都要用代码进行连接WiFi的操作,但这样很不方便,如何使用电脑或手机等设备来实时扫描周围WiFi进行配网?

\n

这里主要用到 WiFiManager库,可以去太极创客下载并加载到ArduinoIDE中。

\n

1、清除WiFi信息

使用前要先清除掉此前连接过的WiFi信息,不然一上电nodeMCU就直接连接上之前的WiFi了

\n

只需2个步骤:

\n
    \n
  1. 创建WiFiManager对象

    \n
  2. \n
  3. 清除WiFi信息

    \n
    1
    2
    WiFiManager wifiManager;\t\t//创建对象
    wifiManager.resetSettings();\t//清除WiFi信息
    \n
  4. \n
\n

完整code

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <ESP8266WiFi.h>          
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

void setup() {
Serial.begin(9600);
// 建立WiFiManager对象
WiFiManager wifiManager;

// 清除ESP8266所存储的WiFi连接信息以便测试WiFiManager工作效果
wifiManager.resetSettings();
Serial.println("ESP8266 WiFi Settings Cleared");
}

void loop() {}
\n

2、WiFi配网

    \n
  1. 创建WiFiManager对象

    \n
  2. \n
  3. 开启接入点模式(就是在开发板上开个热点,让周围设备连接)

    \n
    1
    wifiManager.autoConnect("热点名称"[, "密码"]);
    \n
  4. \n
  5. 接下来就可以用手机、电脑配网

    \n

    (1)用手机(或电脑)连接开发板的热点

    \n

    (2)连接后会自动跳转到一个网页(网页地址就是开发板ip),然后手动选择让开发板连接周围WiFi

    \n

    (3)开发板连接上周围的WiFi后会自动关闭热点,连接不上就会再开启热点,重新配网

    \n

    (4)【注意1】:连接后不能主动更换为其他WiFi,除非该WiFi断开了,开发板才会再次打开热点,否则如要更换WiFi需要擦除已连接的WiFi信息

    \n

    (5)【注意2】:如果连接了校园网WiFi,但需要登录才能用,此时也不能主动断开WiFi去选择其他能用的WiFi,也需要擦除已连接的WiFi信息。

    \n
  6. \n
  7. 判断是否配网成功

    \n

    可以在串口监视屏打印连接上的WiFi名称

    \n
  8. \n
\n

完整code:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <ESP8266WiFi.h>          
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>

void setup() {
Serial.begin(9600);
// 建立WiFiManager对象
WiFiManager wifiManager;

// 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称
wifiManager.autoConnect("ESP8266-wifi");

// 如果您希望该WiFi添加密码,可以使用以下语句:
// wifiManager.autoConnect("ESP8266-wifi", "12345678");


// WiFi连接成功后将通过串口监视器输出连接成功信息
Serial.println("");
Serial.print("ESP8266 Connected to ");
Serial.println(WiFi.SSID()); // 连接上的WiFi名称
Serial.print("IP address:\\t");
Serial.println(WiFi.localIP()); // IP
}

void loop() {}
\n

\"image-20210811095227391\"

\n"},{"title":"MQTT篇(三)关于MQTT的主题","abbrlink":"894e18ee","date":"2021-08-11T04:01:07.000Z","description":"客户端向服务端发布的消息类型、或者要从服务端订阅的消息类型,就是一个个主题,主题在实际应用中用一个字符串来表示","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n## 主题\n\n### 主题基本形式\n\n​\t\t主题基本形式就是一个字符串,比如:currentTime,carSpeed\n\n​\t\t注意:主题区分大小写、可以用空格(但尽量不要用)、尽量使用英文字符\n\n\n\n### 主题分级\n\n​\t\tMQTT主题各个级别之间可以使用`/`来分隔。如:`Tyler-1/motor/1/speed`\n\n​\t\t示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。\n\n\n\n### 主题通配符\n\n​\t\t当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用\n\n- 单级通配符: `+`\t\t示例:\n\n ```sh\n home/sensor/+/temperature\n \n # 可以表示:\n # home/sensor/aaa/temperature\n # home/sensor/bbb/temperature\n # ......\n ```\n\n \n\n- 多级通配符 :`#` 示例:\n\n ```sh\n home/sensor/#\n \n # 可以表示:\n # home/sensor/aaa\n # home/sensor/bbb/ccc\n # home/sensor/aaa/ccc\n ```\n\n \n\n### 注意事项\n\n- 以$开始的主题\n\n 以$开始的主题是MQTT服务端**系统保留的特殊主题**,不能随意订阅或者向其发布信息。如:\n\n ```shell\n $SYS/broker/clients/connected\n $SYS/broker/clients/disconnected\n $SYS/broker/clients/total\n $SYS/broker/messages/sent\n $SYS/broker/uptime\n ...\n ```\n\n- 避免使用`/`作为主题的开头\n\n- 主题中尽量**不要使用空格**\n\n- 主题中尽量使用**ASCII字符**\n\n- 建议在主题中嵌入客户端ID\n 【通过主题中的客户端ID内容,可以很容易的了解该主题信息是由哪一台设备所发布的】\n\n \n\n## ESP8266订阅主题\n\n","source":"_posts/esp8266/MQTT篇(三)-主题.md","raw":"---\ntitle: MQTT篇(三)关于MQTT的主题\ntags:\n - 物联网\n - esp8266\ncategories:\n - 硬件学习\n - 物联网开发\nabbrlink: 894e18ee\ndate: 2021-08-11 12:01:07\ndescription: 客户端向服务端发布的消息类型、或者要从服务端订阅的消息类型,就是一个个主题,主题在实际应用中用一个字符串来表示\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n## 主题\n\n### 主题基本形式\n\n​\t\t主题基本形式就是一个字符串,比如:currentTime,carSpeed\n\n​\t\t注意:主题区分大小写、可以用空格(但尽量不要用)、尽量使用英文字符\n\n\n\n### 主题分级\n\n​\t\tMQTT主题各个级别之间可以使用`/`来分隔。如:`Tyler-1/motor/1/speed`\n\n​\t\t示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。\n\n\n\n### 主题通配符\n\n​\t\t当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用\n\n- 单级通配符: `+`\t\t示例:\n\n ```sh\n home/sensor/+/temperature\n \n # 可以表示:\n # home/sensor/aaa/temperature\n # home/sensor/bbb/temperature\n # ......\n ```\n\n \n\n- 多级通配符 :`#` 示例:\n\n ```sh\n home/sensor/#\n \n # 可以表示:\n # home/sensor/aaa\n # home/sensor/bbb/ccc\n # home/sensor/aaa/ccc\n ```\n\n \n\n### 注意事项\n\n- 以$开始的主题\n\n 以$开始的主题是MQTT服务端**系统保留的特殊主题**,不能随意订阅或者向其发布信息。如:\n\n ```shell\n $SYS/broker/clients/connected\n $SYS/broker/clients/disconnected\n $SYS/broker/clients/total\n $SYS/broker/messages/sent\n $SYS/broker/uptime\n ...\n ```\n\n- 避免使用`/`作为主题的开头\n\n- 主题中尽量**不要使用空格**\n\n- 主题中尽量使用**ASCII字符**\n\n- 建议在主题中嵌入客户端ID\n 【通过主题中的客户端ID内容,可以很容易的了解该主题信息是由哪一台设备所发布的】\n\n \n\n## ESP8266订阅主题\n\n","slug":"esp8266/MQTT篇(三)-主题","published":1,"updated":"2021-08-29T14:54:46.346Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q1004hakve7ci71f3m","content":"

主题

主题基本形式

​ 主题基本形式就是一个字符串,比如:currentTime,carSpeed

\n

​ 注意:主题区分大小写、可以用空格(但尽量不要用)、尽量使用英文字符

\n

主题分级

​ MQTT主题各个级别之间可以使用/来分隔。如:Tyler-1/motor/1/speed

\n

​ 示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。

\n

主题通配符

​ 当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用

\n
    \n
  • 单级通配符: + 示例:

    \n
    1
    2
    3
    4
    5
    6
    home/sensor/+/temperature

    # 可以表示:
    # home/sensor/aaa/temperature
    # home/sensor/bbb/temperature
    # ......
    \n
  • \n
\n
    \n
  • 多级通配符 :# 示例:

    \n
    1
    2
    3
    4
    5
    6
    home/sensor/#

    # 可以表示:
    # home/sensor/aaa
    # home/sensor/bbb/ccc
    # home/sensor/aaa/ccc
    \n
  • \n
\n

注意事项

    \n
  • 以$开始的主题

    \n

    以$开始的主题是MQTT服务端系统保留的特殊主题,不能随意订阅或者向其发布信息。如:

    \n
    1
    2
    3
    4
    5
    6
    $SYS/broker/clients/connected
    $SYS/broker/clients/disconnected
    $SYS/broker/clients/total
    $SYS/broker/messages/sent
    $SYS/broker/uptime
    ...
    \n
  • \n
  • 避免使用/作为主题的开头

    \n
  • \n
  • 主题中尽量不要使用空格

    \n
  • \n
  • 主题中尽量使用ASCII字符

    \n
  • \n
  • 建议在主题中嵌入客户端ID
    【通过主题中的客户端ID内容,可以很容易的了解该主题信息是由哪一台设备所发布的】

    \n
  • \n
\n

ESP8266订阅主题

","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

主题

主题基本形式

​ 主题基本形式就是一个字符串,比如:currentTime,carSpeed

\n

​ 注意:主题区分大小写、可以用空格(但尽量不要用)、尽量使用英文字符

\n

主题分级

​ MQTT主题各个级别之间可以使用/来分隔。如:Tyler-1/motor/1/speed

\n

​ 示例中一共有四级主题,分别是第1级 Tyler-1、第2级motor、第三级1、第4级speed。

\n

主题通配符

​ 当客户端订阅主题时,可以使用通配符同时订阅多个主题。通配符只能在订阅主题时使用

\n
    \n
  • 单级通配符: + 示例:

    \n
    1
    2
    3
    4
    5
    6
    home/sensor/+/temperature

    # 可以表示:
    # home/sensor/aaa/temperature
    # home/sensor/bbb/temperature
    # ......
    \n
  • \n
\n
    \n
  • 多级通配符 :# 示例:

    \n
    1
    2
    3
    4
    5
    6
    home/sensor/#

    # 可以表示:
    # home/sensor/aaa
    # home/sensor/bbb/ccc
    # home/sensor/aaa/ccc
    \n
  • \n
\n

注意事项

    \n
  • 以$开始的主题

    \n

    以$开始的主题是MQTT服务端系统保留的特殊主题,不能随意订阅或者向其发布信息。如:

    \n
    1
    2
    3
    4
    5
    6
    $SYS/broker/clients/connected
    $SYS/broker/clients/disconnected
    $SYS/broker/clients/total
    $SYS/broker/messages/sent
    $SYS/broker/uptime
    ...
    \n
  • \n
  • 避免使用/作为主题的开头

    \n
  • \n
  • 主题中尽量不要使用空格

    \n
  • \n
  • 主题中尽量使用ASCII字符

    \n
  • \n
  • 建议在主题中嵌入客户端ID
    【通过主题中的客户端ID内容,可以很容易的了解该主题信息是由哪一台设备所发布的】

    \n
  • \n
\n

ESP8266订阅主题

"},{"title":"MQTT篇(二)消息的发布、订阅与取消订阅","abbrlink":"adfcabaa","date":"2021-08-10T02:20:32.000Z","description":"MQTT协议指明了客户端与服务端之间信息交流的方式","cover":"https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png","_content":"\n## 发布信息\n\n1. mqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题\n2. 服务端根据主题决定将信息转发给哪些客户端\n\n\n\n![PUBLISH - 发布消息](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-PUBLISH.png)\n\n\n\n发布信息时,会向服务端发送一个**PUBLISH报文**:\n\n![MQTT PUBLISH 报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-Publish-Packet.png)\n\ntopicName:主题名\n\nqos:服务质量等级,分0、1、2共三个等级\n\npacketId:报文标识符,用于区别不同报文\n\n​\t\t【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。\n\nretainFlag:保留标志。\n\n​\t\t一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;\n\n​\t\t特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息\n\n\n\npayLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的\n\n\n\ndupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。\n\n【注意】重发标志只在QoS级别大于0时使用。\n\n## 订阅主题\n\n当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。\n\n客户端是通过向服务端发送**SUBSCRIBE报文**来实现订阅主题\n\n【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。\n\nqos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。\n\nMQTT设备可以通过**“报文标识符”**对MQTT报文进行甄别和管理。\n\n---\n\n**订阅确认:**\n\n服务端接收到客户端的订阅报文后,会向客户端发送**SUBACK报文**确认订阅。\n\n该报文包含**”订阅返回码“**和**”报文标识符“**两个信息\n\n| 返回码 | Return Code Response |\n| :----- | :------------------- |\n| 0 | 订阅成功 – QoS 0 |\n| 1 | 订阅成功- QoS 1 |\n| 2 | 订阅成功- QoS 2 |\n| 128 | 订阅失败 |\n\n---\n\n【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。\n\n**报文标识符**:MQTT设备可以通过该标识符对报文进行管理。\n\n\n\n## 取消订阅\n\n客户端要取消订阅某主题时,可通过向服务端发送**UNSUBSCRIBE** 报文来实现。\n\n![MQTT-UNSUBSCRIBE-取消订阅报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/unsubscribe_packet.png)\n\nUNSUBSCRIBE报文包含两个重要信息:\n\n1. packetId:“报文标识符”\n2. topic1、topic2、…… :取消订阅的主题的名称\n\n\n\n当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。\n\n客户端接收到UNSUBACK报文后就可以确认取消主题订阅\n\n\n\n## ESP8266发布MQTT消息\n\n```c\n#include \n#include \n#include \n\n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"RBook\";\nconst char* password = \"1719171945678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n\n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n\nTicker ticker;\nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n\nint count; // Ticker计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n\n // Ticker定时对象\n ticker.attach(1, tickerCount); \n}\n\nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器\n // 每隔3秒钟发布一次信息\n if (count >= 3){\n pubMQTTmsg();\n count = 0;\n } \n // 保持心跳\n mqttClient.loop();\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n\nvoid tickerCount(){\n count++;\n}\n\nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// 发布信息\nvoid pubMQTTmsg(){\n static int value; // 客户端发布信息用数字\n\n // 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。\n // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,\n String topicString = \"Taichi-Maker-Pub-\" + WiFi.macAddress();\n char publishTopic[topicString.length() + 1]; \n strcpy(publishTopic, topicString.c_str());\n\n // 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。\n String messageString = \"Hello World \" + String(value++); \n char publishMsg[messageString.length() + 1]; \n strcpy(publishMsg, messageString.c_str());\n\n // 实现ESP8266向主题发布信息\n if(mqttClient.publish(publishTopic, publishMsg)){\n Serial.println(\"Publish Topic:\");Serial.println(publishTopic);\n Serial.println(\"Publish message:\");Serial.println(publishMsg); \n } else {\n Serial.println(\"Message Publish Failed.\"); \n }\n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","source":"_posts/esp8266/MQTT篇(二)-发布、订阅、取消订阅.md","raw":"---\ntitle: MQTT篇(二)消息的发布、订阅与取消订阅\ntags:\n - 物联网\n - esp8266\ncategories:\n - - 硬件学习\n - 物联网开发\nabbrlink: adfcabaa\ndate: 2021-08-10 10:20:32\ndescription: MQTT协议指明了客户端与服务端之间信息交流的方式\ncover: https://gitee.com/ajream/images/raw/master/img/20210829225240_pcbimg.png\n---\n\n## 发布信息\n\n1. mqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题\n2. 服务端根据主题决定将信息转发给哪些客户端\n\n\n\n![PUBLISH - 发布消息](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-PUBLISH.png)\n\n\n\n发布信息时,会向服务端发送一个**PUBLISH报文**:\n\n![MQTT PUBLISH 报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/MQTT-Publish-Packet.png)\n\ntopicName:主题名\n\nqos:服务质量等级,分0、1、2共三个等级\n\npacketId:报文标识符,用于区别不同报文\n\n​\t\t【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。\n\nretainFlag:保留标志。\n\n​\t\t一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;\n\n​\t\t特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息\n\n\n\npayLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的\n\n\n\ndupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。\n\n【注意】重发标志只在QoS级别大于0时使用。\n\n## 订阅主题\n\n当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。\n\n客户端是通过向服务端发送**SUBSCRIBE报文**来实现订阅主题\n\n【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。\n\nqos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。\n\nMQTT设备可以通过**“报文标识符”**对MQTT报文进行甄别和管理。\n\n---\n\n**订阅确认:**\n\n服务端接收到客户端的订阅报文后,会向客户端发送**SUBACK报文**确认订阅。\n\n该报文包含**”订阅返回码“**和**”报文标识符“**两个信息\n\n| 返回码 | Return Code Response |\n| :----- | :------------------- |\n| 0 | 订阅成功 – QoS 0 |\n| 1 | 订阅成功- QoS 1 |\n| 2 | 订阅成功- QoS 2 |\n| 128 | 订阅失败 |\n\n---\n\n【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。\n\n**报文标识符**:MQTT设备可以通过该标识符对报文进行管理。\n\n\n\n## 取消订阅\n\n客户端要取消订阅某主题时,可通过向服务端发送**UNSUBSCRIBE** 报文来实现。\n\n![MQTT-UNSUBSCRIBE-取消订阅报文](http://www.taichi-maker.com/wp-content/uploads/2020/09/unsubscribe_packet.png)\n\nUNSUBSCRIBE报文包含两个重要信息:\n\n1. packetId:“报文标识符”\n2. topic1、topic2、…… :取消订阅的主题的名称\n\n\n\n当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。\n\n客户端接收到UNSUBACK报文后就可以确认取消主题订阅\n\n\n\n## ESP8266发布MQTT消息\n\n```c\n#include \n#include \n#include \n\n// 设置wifi接入信息(请根据您的WiFi信息进行修改)\nconst char* ssid = \"RBook\";\nconst char* password = \"1719171945678\";\nconst char* mqttServer = \"test.ranye-iot.net\";\n\n// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案\n// http://www.taichi-maker.com/public-mqtt-broker/\n\nTicker ticker;\nWiFiClient wifiClient;\nPubSubClient mqttClient(wifiClient);\n\nint count; // Ticker计数用变量\n\nvoid setup() {\n Serial.begin(9600);\n\n //设置ESP8266工作模式为无线终端模式\n WiFi.mode(WIFI_STA);\n\n // 连接WiFi\n connectWifi();\n\n // 设置MQTT服务器和端口号\n mqttClient.setServer(mqttServer, 1883);\n\n // 连接MQTT服务器\n connectMQTTServer();\n\n // Ticker定时对象\n ticker.attach(1, tickerCount); \n}\n\nvoid loop() { \n if (mqttClient.connected()) { // 如果开发板成功连接服务器\n // 每隔3秒钟发布一次信息\n if (count >= 3){\n pubMQTTmsg();\n count = 0;\n } \n // 保持心跳\n mqttClient.loop();\n } else { // 如果开发板未能成功连接服务器\n connectMQTTServer(); // 则尝试连接服务器\n }\n}\n\nvoid tickerCount(){\n count++;\n}\n\nvoid connectMQTTServer(){\n // 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)\n String clientId = \"esp8266-\" + WiFi.macAddress();\n\n // 连接MQTT服务器\n if (mqttClient.connect(clientId.c_str())) { \n Serial.println(\"MQTT Server Connected.\");\n Serial.println(\"Server Address: \");\n Serial.println(mqttServer);\n Serial.println(\"ClientId:\");\n Serial.println(clientId);\n } else {\n Serial.print(\"MQTT Server Connect Failed. Client State:\");\n Serial.println(mqttClient.state());\n delay(3000);\n } \n}\n\n// 发布信息\nvoid pubMQTTmsg(){\n static int value; // 客户端发布信息用数字\n\n // 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。\n // 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,\n String topicString = \"Taichi-Maker-Pub-\" + WiFi.macAddress();\n char publishTopic[topicString.length() + 1]; \n strcpy(publishTopic, topicString.c_str());\n\n // 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。\n String messageString = \"Hello World \" + String(value++); \n char publishMsg[messageString.length() + 1]; \n strcpy(publishMsg, messageString.c_str());\n\n // 实现ESP8266向主题发布信息\n if(mqttClient.publish(publishTopic, publishMsg)){\n Serial.println(\"Publish Topic:\");Serial.println(publishTopic);\n Serial.println(\"Publish message:\");Serial.println(publishMsg); \n } else {\n Serial.println(\"Message Publish Failed.\"); \n }\n}\n\n// ESP8266连接wifi\nvoid connectWifi(){\n\n WiFi.begin(ssid, password);\n\n //等待WiFi连接,成功连接后输出成功信息\n while (WiFi.status() != WL_CONNECTED) {\n delay(1000);\n Serial.print(\".\");\n }\n Serial.println(\"\");\n Serial.println(\"WiFi Connected!\"); \n Serial.println(\"\"); \n}\n```\n\n","slug":"esp8266/MQTT篇(二)-发布、订阅、取消订阅","published":1,"updated":"2021-08-29T14:54:42.648Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q2004jakvedojmd1vx","content":"

发布信息

    \n
  1. mqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题
  2. \n
  3. 服务端根据主题决定将信息转发给哪些客户端
  4. \n
\n

\"PUBLISH

\n

发布信息时,会向服务端发送一个PUBLISH报文

\n

\"MQTT

\n

topicName:主题名

\n

qos:服务质量等级,分0、1、2共三个等级

\n

packetId:报文标识符,用于区别不同报文

\n

​ 【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。

\n

retainFlag:保留标志。

\n

​ 一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;

\n

​ 特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息

\n

payLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的

\n

dupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。

\n

【注意】重发标志只在QoS级别大于0时使用。

\n

订阅主题

当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。

\n

客户端是通过向服务端发送SUBSCRIBE报文来实现订阅主题

\n

【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。

\n

qos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。

\n

MQTT设备可以通过“报文标识符”对MQTT报文进行甄别和管理。

\n
\n

订阅确认:

\n

服务端接收到客户端的订阅报文后,会向客户端发送SUBACK报文确认订阅。

\n

该报文包含”订阅返回码“”报文标识符“两个信息

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
返回码Return Code Response
0订阅成功 – QoS 0
1订阅成功- QoS 1
2订阅成功- QoS 2
128订阅失败
\n
\n
\n

【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。

\n

报文标识符:MQTT设备可以通过该标识符对报文进行管理。

\n

取消订阅

客户端要取消订阅某主题时,可通过向服务端发送UNSUBSCRIBE 报文来实现。

\n

\"MQTT-UNSUBSCRIBE-取消订阅报文\"

\n

UNSUBSCRIBE报文包含两个重要信息:

\n
    \n
  1. packetId:“报文标识符”
  2. \n
  3. topic1、topic2、…… :取消订阅的主题的名称
  4. \n
\n

当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。

\n

客户端接收到UNSUBACK报文后就可以确认取消主题订阅

\n

ESP8266发布MQTT消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "RBook";
const char* password = "1719171945678";
const char* mqttServer = "test.ranye-iot.net";

// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

Ticker ticker;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

int count; // Ticker计数用变量

void setup() {
Serial.begin(9600);

//设置ESP8266工作模式为无线终端模式
WiFi.mode(WIFI_STA);

// 连接WiFi
connectWifi();

// 设置MQTT服务器和端口号
mqttClient.setServer(mqttServer, 1883);

// 连接MQTT服务器
connectMQTTServer();

// Ticker定时对象
ticker.attach(1, tickerCount);
}

void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
// 每隔3秒钟发布一次信息
if (count >= 3){
pubMQTTmsg();
count = 0;
}
// 保持心跳
mqttClient.loop();
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}

void tickerCount(){
count++;
}

void connectMQTTServer(){
// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
String clientId = "esp8266-" + WiFi.macAddress();

// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}

// 发布信息
void pubMQTTmsg(){
static int value; // 客户端发布信息用数字

// 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。
// 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,
String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress();
char publishTopic[topicString.length() + 1];
strcpy(publishTopic, topicString.c_str());

// 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。
String messageString = "Hello World " + String(value++);
char publishMsg[messageString.length() + 1];
strcpy(publishMsg, messageString.c_str());

// 实现ESP8266向主题发布信息
if(mqttClient.publish(publishTopic, publishMsg)){
Serial.println("Publish Topic:");Serial.println(publishTopic);
Serial.println("Publish message:");Serial.println(publishMsg);
} else {
Serial.println("Message Publish Failed.");
}
}

// ESP8266连接wifi
void connectWifi(){

WiFi.begin(ssid, password);

//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

发布信息

    \n
  1. mqtt客户端连接到服务端后即可发布信息,每条信息必须包含一个主题
  2. \n
  3. 服务端根据主题决定将信息转发给哪些客户端
  4. \n
\n

\"PUBLISH

\n

发布信息时,会向服务端发送一个PUBLISH报文

\n

\"MQTT

\n

topicName:主题名

\n

qos:服务质量等级,分0、1、2共三个等级

\n

packetId:报文标识符,用于区别不同报文

\n

​ 【注意】:报文标识符的内容与QoS级别有密不可分的关系。只有QoS级别大于0时,报文标识符才是非零数值。如果QoS等于0,报文标识符为0。

\n

retainFlag:保留标志。

\n

​ 一般情况,客户端订阅某个主题的信息后,服务器不会立刻返回该主题的信息,要等服务器收到新信息时才会返回;

\n

​ 特殊情况,我们要求客户端订阅一个主题后,服务器就要立刻返回新信息

\n

payLoad:有效载荷,可以使用mqtt协议发送文本、图片等内容,这些内容是通过payLoad来发送的

\n

dupFlag:重发标志。当接收方没有及时确认收到报文时,发送方会重复发送MQTT报文。在重复发送MQTT报文时,发送方会将此“重发标志”设置为true。

\n

【注意】重发标志只在QoS级别大于0时使用。

\n

订阅主题

当客户端连接到服务端后,除了可以发布消息,也可以接收消息,而客户端要想接收消息,首先要订阅该消息的主题。

\n

客户端是通过向服务端发送SUBSCRIBE报文来实现订阅主题

\n

【注意】一个SUBSCRIBE报文可以包含有单个或者多个订阅主题名。

\n

qos:客户端在订阅主题时也可以明确QoS。服务端会根据SUBSCRIBE中的QoS来提供相应的服务保证。

\n

MQTT设备可以通过“报文标识符”对MQTT报文进行甄别和管理。

\n
\n

订阅确认:

\n

服务端接收到客户端的订阅报文后,会向客户端发送SUBACK报文确认订阅。

\n

该报文包含”订阅返回码“”报文标识符“两个信息

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
返回码Return Code Response
0订阅成功 – QoS 0
1订阅成功- QoS 1
2订阅成功- QoS 2
128订阅失败
\n
\n
\n

【注意】针对不同的主题订阅QoS,服务端的返回码会有所不同。

\n

报文标识符:MQTT设备可以通过该标识符对报文进行管理。

\n

取消订阅

客户端要取消订阅某主题时,可通过向服务端发送UNSUBSCRIBE 报文来实现。

\n

\"MQTT-UNSUBSCRIBE-取消订阅报文\"

\n

UNSUBSCRIBE报文包含两个重要信息:

\n
    \n
  1. packetId:“报文标识符”
  2. \n
  3. topic1、topic2、…… :取消订阅的主题的名称
  4. \n
\n

当服务端接收到UNSUBSCRIBE报文后,会向客户端发送取消订阅确认报文 – UNSUBACK报文。该报文含有客户端所发送的“取消订阅报文标识符”。

\n

客户端接收到UNSUBACK报文后就可以确认取消主题订阅

\n

ESP8266发布MQTT消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>

// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char* ssid = "RBook";
const char* password = "1719171945678";
const char* mqttServer = "test.ranye-iot.net";

// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/

Ticker ticker;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

int count; // Ticker计数用变量

void setup() {
Serial.begin(9600);

//设置ESP8266工作模式为无线终端模式
WiFi.mode(WIFI_STA);

// 连接WiFi
connectWifi();

// 设置MQTT服务器和端口号
mqttClient.setServer(mqttServer, 1883);

// 连接MQTT服务器
connectMQTTServer();

// Ticker定时对象
ticker.attach(1, tickerCount);
}

void loop() {
if (mqttClient.connected()) { // 如果开发板成功连接服务器
// 每隔3秒钟发布一次信息
if (count >= 3){
pubMQTTmsg();
count = 0;
}
// 保持心跳
mqttClient.loop();
} else { // 如果开发板未能成功连接服务器
connectMQTTServer(); // 则尝试连接服务器
}
}

void tickerCount(){
count++;
}

void connectMQTTServer(){
// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)
String clientId = "esp8266-" + WiFi.macAddress();

// 连接MQTT服务器
if (mqttClient.connect(clientId.c_str())) {
Serial.println("MQTT Server Connected.");
Serial.println("Server Address: ");
Serial.println(mqttServer);
Serial.println("ClientId:");
Serial.println(clientId);
} else {
Serial.print("MQTT Server Connect Failed. Client State:");
Serial.println(mqttClient.state());
delay(3000);
}
}

// 发布信息
void pubMQTTmsg(){
static int value; // 客户端发布信息用数字

// 建立发布主题。主题名称以Taichi-Maker-为前缀,后面添加设备的MAC地址。
// 这么做是为确保不同用户进行MQTT信息发布时,ESP8266客户端名称各不相同,
String topicString = "Taichi-Maker-Pub-" + WiFi.macAddress();
char publishTopic[topicString.length() + 1];
strcpy(publishTopic, topicString.c_str());

// 建立发布信息。信息内容以Hello World为起始,后面添加发布次数。
String messageString = "Hello World " + String(value++);
char publishMsg[messageString.length() + 1];
strcpy(publishMsg, messageString.c_str());

// 实现ESP8266向主题发布信息
if(mqttClient.publish(publishTopic, publishMsg)){
Serial.println("Publish Topic:");Serial.println(publishTopic);
Serial.println("Publish message:");Serial.println(publishMsg);
} else {
Serial.println("Message Publish Failed.");
}
}

// ESP8266连接wifi
void connectWifi(){

WiFi.begin(ssid, password);

//等待WiFi连接,成功连接后输出成功信息
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Connected!");
Serial.println("");
}
\n"},{"title":"SpringMVC(一)快速入门","description":"springMVC概述、组件","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"890c8bdb","date":"2021-09-01T11:31:26.000Z","_content":"\n\n\n\n\n\n\n## 概述\n\nSpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便\n\n- 与Spring兼容性好,无缝结合\n\n- 约定优于配置\n\n\n\n## MVC设计模式\n\n\n\nMVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。\n\n使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。\n\n\n\n{% note info flat %}\nController用于接收客户端请求,调用Model生成业务数据,传递给View\n{% endnote %}\n\n\n\nspringMVC架构\n\n![springMVC架构](https://gitee.com/ajream/images/raw/master/img/20210902234928_1-1601161F914292.png)\n\n\n\n## 核心组件\n\nDispatcherServlet:\n\nMVC框架是围绕`DispatcherServlet`设计的,它处理所有的HTTP请求和响应。 **Spring Web MVC DispatcherServlet**的请求处理工作流如下图所示:\n\n![img](http://www.yiibai.com/uploads/images/201701/18/451110124_66519.png)\n\n以下是对应于到`DispatcherServlet`的传入HTTP请求的事件顺序:\n\n1. 在接收到HTTP请求后,`DispatcherServlet`会查询`HandlerMapping`以调用相应的`Controller`。\n2. `Controller`接受请求并根据使用的`GET`或`POST`方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给`DispatcherServlet`\n3. `DispatcherServlet`将从`ViewResolver`获取请求的定义视图。\n4. 当视图完成,`DispatcherServlet`将模型数据传递到最终的视图,并在浏览器上呈现。\n\n所有上述组件,即: `HandlerMapping`,`Controller`和`ViewResolver`是`WebApplicationContext`的一部分,它是普通`ApplicationContext`的扩展,带有Web应用程序所需的一些额外功能。\n\n\n\n{%folding blue, 配置%}\n\n\n\n需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。\n\n下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:\n\n\n\n```xml\n\n\n Spring MVC Application\n\n \n HelloWeb\n \n org.springframework.web.servlet.DispatcherServlet\n \n 1\n \n\n \n HelloWeb\n *.jsp\n \n\n\n```\n\n\n\n`web.xml`文件将保存Web应用程序的`WebContent/WEB-INF`目录。\n\n在`HelloWeb DispatcherServlet`初始化时,框架将尝试从位于应用程序的`WebContent/WEB-INF`目录中的名为`[servlet-name]-servlet.xml`的文件加载应用程序上下文。在这个示例中,使用的文件将是`HelloWeb-servlet.xml`。\n\n接下来,``标记指示哪些`URL`将由`DispatcherServlet`处理。 这里所有以`.jsp`结尾的HTTP请求都将由`HelloWeb DispatcherServlet`处理。\n\n\n\n如果不想使用默认文件名为`[servlet-name]-servlet.xml`和默认位置为`WebContent/WEB-INF`,可以通过在`web.xml`文件中添加`servlet`侦听器`ContextLoaderListener`来自定义此文件名和位置 如下\n\n\n\n```xml\n\n\n\n....\n\n contextConfigLocation\n /WEB-INF/HelloWeb-servlet.xml\n\n\n\n \n org.springframework.web.context.ContextLoaderListener\n \n\n\n```\n\n\n\n{%endfolding%}\n\n## 回顾创建一个Servlet程序\n\n[项目地址springMVC-01-Servlet](https://codechina.csdn.net/m0_46079750/springmvc)\n\n\n\n{% tabs 第一个springMVC程序%}\n\n\n\n\n\n```xml\n\n \n org.springframework\n spring-webmvc\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.13\n \n \n javax.servlet\n servlet-api\n 2.5\n \n \n javax.servlet.jsp\n jsp-api\n 2.2\n \n \n javax.servlet\n jstl\n 1.2\n \n\n```\n\n\n\n{%folding blue, note:普通maven项目转为web项目%}\n\n1. 右键该项目,选择 `Add Framework support `\n\n ![image-20210901210441382](https://gitee.com/ajream/images/raw/master/img/20210901210444_image-20210901210441382.png)\n\n2. 选择 `Web Application`-> `OK`\n\n ![image-20210901210605521](https://gitee.com/ajream/images/raw/master/img/20210901210606_image-20210901210605521.png)\n\n3. 结果生成 `web`文件夹,说明成功了\n\n ![image-20210901210837382](https://gitee.com/ajream/images/raw/master/img/20210901210838_image-20210901210837382.png)\n\n\n\n\n\n{%endfolding%}\n\n\n\n\n\n\n\n1. 实现HttpServlet接口\n\n ```java\n package com.ajream.sevlet;\n \n import javax.servlet.ServletException;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n public class HelloServlet extends HttpServlet {\n \n @Override\n protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n // 1. 获取前端参数\n String method = req.getParameter(\"method\");\n if(method.equals(\"add\")){\n req.getSession().setAttribute(\"msg\", \"执行了add方法\");\n }\n if(method.equals(\"delete\")){\n req.getSession().setAttribute(\"msg\", \"执行了delete方法\");\n }\n // 2. 调用业务层\n // 3. 视图转发或者重定向\n req.getRequestDispatcher(\"WEB-INF/jsp/test.jsp\").forward(req,resp);\n \n }\n @Override\n protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n doGet(req, resp);\n }\n }\n \n ```\n\n2. 新建视图文件 `web/WEB-INF/jsp/test.jsp`\n\n ```html\n \n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title\n \n \n ${msg}\n \n \n \n ```\n\n3. 配置 `web/WEB-INF/web.xml`\n\n ```xml\n \n \n \n hello\n com.ajream.sevlet.HelloServlet\n \n \n \n hello\n /hello\n \n \n \n \n \n \n \n index.jsp \n \n \n ```\n\n4. 新建表单 `web/WEB-INF/jsp/form.jsp`\n\n ```html\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title\n \n \n \n
\n \n \n
\n \n \n \n \n ```\n\n \n\n\n\n\n\n\n\n![image-20210902000157563](https://gitee.com/ajream/images/raw/master/img/20210902000200_image-20210902000157563.png)\n\n\n\n![image-20210902000426095](https://gitee.com/ajream/images/raw/master/img/20210902000427_image-20210902000426095.png)\n\n\n\n在 `Application context` 输入: `/`\n\n![image-20210902000536524](https://gitee.com/ajream/images/raw/master/img/20210902000537_image-20210902000536524.png)\n\n\n\n\n\n\n\n\n\n在浏览器输入:`http://localhost:8083/hello?method=add`,(端口号可能不同)\n\n结果如下:\n\n![image-20210902000703371](https://gitee.com/ajream/images/raw/master/img/20210902000704_image-20210902000703371.png)\n\n\n\n\n\n{%endtabs%}\n\n\n\n","source":"_posts/SpringMVC/springMVC(一)快速入门.md","raw":"---\ntitle: SpringMVC(一)快速入门\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: springMVC概述、组件\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: 890c8bdb\ndate: 2021-09-01 19:31:26\n---\n\n\n\n\n\n\n\n## 概述\n\nSpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便\n\n- 与Spring兼容性好,无缝结合\n\n- 约定优于配置\n\n\n\n## MVC设计模式\n\n\n\nMVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。\n\n使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。\n\n\n\n{% note info flat %}\nController用于接收客户端请求,调用Model生成业务数据,传递给View\n{% endnote %}\n\n\n\nspringMVC架构\n\n![springMVC架构](https://gitee.com/ajream/images/raw/master/img/20210902234928_1-1601161F914292.png)\n\n\n\n## 核心组件\n\nDispatcherServlet:\n\nMVC框架是围绕`DispatcherServlet`设计的,它处理所有的HTTP请求和响应。 **Spring Web MVC DispatcherServlet**的请求处理工作流如下图所示:\n\n![img](http://www.yiibai.com/uploads/images/201701/18/451110124_66519.png)\n\n以下是对应于到`DispatcherServlet`的传入HTTP请求的事件顺序:\n\n1. 在接收到HTTP请求后,`DispatcherServlet`会查询`HandlerMapping`以调用相应的`Controller`。\n2. `Controller`接受请求并根据使用的`GET`或`POST`方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给`DispatcherServlet`\n3. `DispatcherServlet`将从`ViewResolver`获取请求的定义视图。\n4. 当视图完成,`DispatcherServlet`将模型数据传递到最终的视图,并在浏览器上呈现。\n\n所有上述组件,即: `HandlerMapping`,`Controller`和`ViewResolver`是`WebApplicationContext`的一部分,它是普通`ApplicationContext`的扩展,带有Web应用程序所需的一些额外功能。\n\n\n\n{%folding blue, 配置%}\n\n\n\n需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。\n\n下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:\n\n\n\n```xml\n\n\n Spring MVC Application\n\n \n HelloWeb\n \n org.springframework.web.servlet.DispatcherServlet\n \n 1\n \n\n \n HelloWeb\n *.jsp\n \n\n\n```\n\n\n\n`web.xml`文件将保存Web应用程序的`WebContent/WEB-INF`目录。\n\n在`HelloWeb DispatcherServlet`初始化时,框架将尝试从位于应用程序的`WebContent/WEB-INF`目录中的名为`[servlet-name]-servlet.xml`的文件加载应用程序上下文。在这个示例中,使用的文件将是`HelloWeb-servlet.xml`。\n\n接下来,``标记指示哪些`URL`将由`DispatcherServlet`处理。 这里所有以`.jsp`结尾的HTTP请求都将由`HelloWeb DispatcherServlet`处理。\n\n\n\n如果不想使用默认文件名为`[servlet-name]-servlet.xml`和默认位置为`WebContent/WEB-INF`,可以通过在`web.xml`文件中添加`servlet`侦听器`ContextLoaderListener`来自定义此文件名和位置 如下\n\n\n\n```xml\n\n\n\n....\n\n contextConfigLocation\n /WEB-INF/HelloWeb-servlet.xml\n\n\n\n \n org.springframework.web.context.ContextLoaderListener\n \n\n\n```\n\n\n\n{%endfolding%}\n\n## 回顾创建一个Servlet程序\n\n[项目地址springMVC-01-Servlet](https://codechina.csdn.net/m0_46079750/springmvc)\n\n\n\n{% tabs 第一个springMVC程序%}\n\n\n\n\n\n```xml\n\n \n org.springframework\n spring-webmvc\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.13\n \n \n javax.servlet\n servlet-api\n 2.5\n \n \n javax.servlet.jsp\n jsp-api\n 2.2\n \n \n javax.servlet\n jstl\n 1.2\n \n\n```\n\n\n\n{%folding blue, note:普通maven项目转为web项目%}\n\n1. 右键该项目,选择 `Add Framework support `\n\n ![image-20210901210441382](https://gitee.com/ajream/images/raw/master/img/20210901210444_image-20210901210441382.png)\n\n2. 选择 `Web Application`-> `OK`\n\n ![image-20210901210605521](https://gitee.com/ajream/images/raw/master/img/20210901210606_image-20210901210605521.png)\n\n3. 结果生成 `web`文件夹,说明成功了\n\n ![image-20210901210837382](https://gitee.com/ajream/images/raw/master/img/20210901210838_image-20210901210837382.png)\n\n\n\n\n\n{%endfolding%}\n\n\n\n\n\n\n\n1. 实现HttpServlet接口\n\n ```java\n package com.ajream.sevlet;\n \n import javax.servlet.ServletException;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n public class HelloServlet extends HttpServlet {\n \n @Override\n protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n // 1. 获取前端参数\n String method = req.getParameter(\"method\");\n if(method.equals(\"add\")){\n req.getSession().setAttribute(\"msg\", \"执行了add方法\");\n }\n if(method.equals(\"delete\")){\n req.getSession().setAttribute(\"msg\", \"执行了delete方法\");\n }\n // 2. 调用业务层\n // 3. 视图转发或者重定向\n req.getRequestDispatcher(\"WEB-INF/jsp/test.jsp\").forward(req,resp);\n \n }\n @Override\n protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n doGet(req, resp);\n }\n }\n \n ```\n\n2. 新建视图文件 `web/WEB-INF/jsp/test.jsp`\n\n ```html\n \n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title\n \n \n ${msg}\n \n \n \n ```\n\n3. 配置 `web/WEB-INF/web.xml`\n\n ```xml\n \n \n \n hello\n com.ajream.sevlet.HelloServlet\n \n \n \n hello\n /hello\n \n \n \n \n \n \n \n index.jsp \n \n \n ```\n\n4. 新建表单 `web/WEB-INF/jsp/form.jsp`\n\n ```html\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n Title\n \n \n \n
\n \n \n
\n \n \n \n \n ```\n\n \n\n\n\n\n\n\n\n![image-20210902000157563](https://gitee.com/ajream/images/raw/master/img/20210902000200_image-20210902000157563.png)\n\n\n\n![image-20210902000426095](https://gitee.com/ajream/images/raw/master/img/20210902000427_image-20210902000426095.png)\n\n\n\n在 `Application context` 输入: `/`\n\n![image-20210902000536524](https://gitee.com/ajream/images/raw/master/img/20210902000537_image-20210902000536524.png)\n\n\n\n\n\n\n\n\n\n在浏览器输入:`http://localhost:8083/hello?method=add`,(端口号可能不同)\n\n结果如下:\n\n![image-20210902000703371](https://gitee.com/ajream/images/raw/master/img/20210902000704_image-20210902000703371.png)\n\n\n\n\n\n{%endtabs%}\n\n\n\n","slug":"SpringMVC/springMVC(一)快速入门","published":1,"updated":"2021-09-05T10:33:47.417Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q4004makve8gio9l5r","content":"

概述

SpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便

\n
    \n
  • 与Spring兼容性好,无缝结合

    \n
  • \n
  • 约定优于配置

    \n
  • \n
\n

MVC设计模式

MVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。

\n

使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。

\n

Controller用于接收客户端请求,调用Model生成业务数据,传递给View

\n
\n

springMVC架构

\n

\"springMVC架构\"

\n

核心组件

DispatcherServlet:

\n

MVC框架是围绕DispatcherServlet设计的,它处理所有的HTTP请求和响应。 Spring Web MVC DispatcherServlet的请求处理工作流如下图所示:

\n

\"img\"

\n

以下是对应于到DispatcherServlet的传入HTTP请求的事件顺序:

\n
    \n
  1. 在接收到HTTP请求后,DispatcherServlet会查询HandlerMapping以调用相应的Controller
  2. \n
  3. Controller接受请求并根据使用的GETPOST方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给DispatcherServlet
  4. \n
  5. DispatcherServlet将从ViewResolver获取请求的定义视图。
  6. \n
  7. 当视图完成,DispatcherServlet将模型数据传递到最终的视图,并在浏览器上呈现。
  8. \n
\n

所有上述组件,即: HandlerMappingControllerViewResolverWebApplicationContext的一部分,它是普通ApplicationContext的扩展,带有Web应用程序所需的一些额外功能。

\n
配置 \n
\n

需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。

下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<display-name>Spring MVC Application</display-name>

<servlet>
<servlet-name>HelloWeb</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>HelloWeb</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>

</web-app>

web.xml文件将保存Web应用程序的WebContent/WEB-INF目录。

HelloWeb DispatcherServlet初始化时,框架将尝试从位于应用程序的WebContent/WEB-INF目录中的名为[servlet-name]-servlet.xml的文件加载应用程序上下文。在这个示例中,使用的文件将是HelloWeb-servlet.xml

接下来,<servlet-mapping>标记指示哪些URL将由DispatcherServlet处理。 这里所有以.jsp结尾的HTTP请求都将由HelloWeb DispatcherServlet处理。

如果不想使用默认文件名为[servlet-name]-servlet.xml和默认位置为WebContent/WEB-INF,可以通过在web.xml文件中添加servlet侦听器ContextLoaderListener来自定义此文件名和位置 如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<web-app...>

<!-------- DispatcherServlet definition goes here----->
....
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/HelloWeb-servlet.xml</param-value>
</context-param>

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
\n
\n
\n

回顾创建一个Servlet程序

项目地址springMVC-01-Servlet

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
\n
note:普通maven项目转为web项目 \n
\n
  1. 右键该项目,选择 Add Framework support

    \"image-20210901210441382\"

  2. 选择 Web Application-> OK

    \"image-20210901210605521\"

  3. 结果生成 web文件夹,说明成功了

    \"image-20210901210837382\"

\n
\n
    \n
  1. 实现HttpServlet接口

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package com.ajream.sevlet;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 1. 获取前端参数
    String method = req.getParameter("method");
    if(method.equals("add")){
    req.getSession().setAttribute("msg", "执行了add方法");
    }
    if(method.equals("delete")){
    req.getSession().setAttribute("msg", "执行了delete方法");
    }
    // 2. 调用业务层
    // 3. 视图转发或者重定向
    req.getRequestDispatcher("WEB-INF/jsp/test.jsp").forward(req,resp);

    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doGet(req, resp);
    }
    }

    \n
  2. \n
  3. 新建视图文件 web/WEB-INF/jsp/test.jsp

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>

    \n
  4. \n
  5. 配置 web/WEB-INF/web.xml

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.ajream.sevlet.HelloServlet</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <!-- <session-config>-->
    <!-- <session-timeout>15</session-timeout> &lt;!&ndash;15分钟超时&ndash;&gt;-->
    <!-- </session-config>-->

    <welcome-file-list>
    <welcome-file>index.jsp</welcome-file> <!--配置欢迎页面-->
    </welcome-file-list>
    </web-app>
    \n
  6. \n
  7. 新建表单 web/WEB-INF/jsp/form.jsp

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>

    <form action="/hello" method="post">
    <input type="text" name="method">
    <input type="submit">
    </form>

    </body>
    </html>

    \n
  8. \n

\"image-20210902000157563\"

\n

\"image-20210902000426095\"

\n

Application context 输入: /

\n

\"image-20210902000536524\"

在浏览器输入:http://localhost:8083/hello?method=add,(端口号可能不同)

\n

结果如下:

\n

\"image-20210902000703371\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

概述

SpringMVC是主流的实现MVC设计模式的企业级开发框架,是spring框架的一个子模块,无需整合,开发起来方便

\n
    \n
  • 与Spring兼容性好,无缝结合

    \n
  • \n
  • 约定优于配置

    \n
  • \n
\n

MVC设计模式

MVC 设计模式一般指 MVC 框架,M(Model)指数据模型层,V(View)指视图层,C(Controller)指控制层。

\n

使用 MVC 的目的是将 M 和 V 的实现代码分离,使同一个程序可以有不同的表现形式。其中,View 的定义比较清晰,就是用户界面。

\n

Controller用于接收客户端请求,调用Model生成业务数据,传递给View

\n
\n

springMVC架构

\n

\"springMVC架构\"

\n

核心组件

DispatcherServlet:

\n

MVC框架是围绕DispatcherServlet设计的,它处理所有的HTTP请求和响应。 Spring Web MVC DispatcherServlet的请求处理工作流如下图所示:

\n

\"img\"

\n

以下是对应于到DispatcherServlet的传入HTTP请求的事件顺序:

\n
    \n
  1. 在接收到HTTP请求后,DispatcherServlet会查询HandlerMapping以调用相应的Controller
  2. \n
  3. Controller接受请求并根据使用的GETPOST方法调用相应的服务方法。 服务方法将基于定义的业务逻辑设置模型数据,并将视图名称返回给DispatcherServlet
  4. \n
  5. DispatcherServlet将从ViewResolver获取请求的定义视图。
  6. \n
  7. 当视图完成,DispatcherServlet将模型数据传递到最终的视图,并在浏览器上呈现。
  8. \n
\n

所有上述组件,即: HandlerMappingControllerViewResolverWebApplicationContext的一部分,它是普通ApplicationContext的扩展,带有Web应用程序所需的一些额外功能。

\n
配置 \n
\n

需要通过使用web.xml文件中的URL映射来映射希望DispatcherServlet处理的请求。

下面是一个示例来显示HelloWeb DispatcherServlet示例的声明和映射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<display-name>Spring MVC Application</display-name>

<servlet>
<servlet-name>HelloWeb</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>HelloWeb</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>

</web-app>

web.xml文件将保存Web应用程序的WebContent/WEB-INF目录。

HelloWeb DispatcherServlet初始化时,框架将尝试从位于应用程序的WebContent/WEB-INF目录中的名为[servlet-name]-servlet.xml的文件加载应用程序上下文。在这个示例中,使用的文件将是HelloWeb-servlet.xml

接下来,<servlet-mapping>标记指示哪些URL将由DispatcherServlet处理。 这里所有以.jsp结尾的HTTP请求都将由HelloWeb DispatcherServlet处理。

如果不想使用默认文件名为[servlet-name]-servlet.xml和默认位置为WebContent/WEB-INF,可以通过在web.xml文件中添加servlet侦听器ContextLoaderListener来自定义此文件名和位置 如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<web-app...>

<!-------- DispatcherServlet definition goes here----->
....
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/HelloWeb-servlet.xml</param-value>
</context-param>

<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
\n
\n
\n

回顾创建一个Servlet程序

项目地址springMVC-01-Servlet

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
\n
note:普通maven项目转为web项目 \n
\n
  1. 右键该项目,选择 Add Framework support

    \"image-20210901210441382\"

  2. 选择 Web Application-> OK

    \"image-20210901210605521\"

  3. 结果生成 web文件夹,说明成功了

    \"image-20210901210837382\"

\n
\n
    \n
  1. 实现HttpServlet接口

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package com.ajream.sevlet;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 1. 获取前端参数
    String method = req.getParameter("method");
    if(method.equals("add")){
    req.getSession().setAttribute("msg", "执行了add方法");
    }
    if(method.equals("delete")){
    req.getSession().setAttribute("msg", "执行了delete方法");
    }
    // 2. 调用业务层
    // 3. 视图转发或者重定向
    req.getRequestDispatcher("WEB-INF/jsp/test.jsp").forward(req,resp);

    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doGet(req, resp);
    }
    }

    \n
  2. \n
  3. 新建视图文件 web/WEB-INF/jsp/test.jsp

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>

    \n
  4. \n
  5. 配置 web/WEB-INF/web.xml

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.ajream.sevlet.HelloServlet</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <!-- <session-config>-->
    <!-- <session-timeout>15</session-timeout> &lt;!&ndash;15分钟超时&ndash;&gt;-->
    <!-- </session-config>-->

    <welcome-file-list>
    <welcome-file>index.jsp</welcome-file> <!--配置欢迎页面-->
    </welcome-file-list>
    </web-app>
    \n
  6. \n
  7. 新建表单 web/WEB-INF/jsp/form.jsp

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>Title</title>
    </head>
    <body>

    <form action="/hello" method="post">
    <input type="text" name="method">
    <input type="submit">
    </form>

    </body>
    </html>

    \n
  8. \n

\"image-20210902000157563\"

\n

\"image-20210902000426095\"

\n

Application context 输入: /

\n

\"image-20210902000536524\"

在浏览器输入:http://localhost:8083/hello?method=add,(端口号可能不同)

\n

结果如下:

\n

\"image-20210902000703371\"

\n"},{"title":"SpringMVC(三)HelloMVC(注解版)","description":"SpringMVC(注解版本)的HelloWorld程序","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"b8426075","date":"2021-09-03T07:01:20.000Z","_content":"\n\n\n\n\n\n\n[项目地址springMVC-03-hello-annotetion](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 新建一个project:`springmvc-03-hello-annotation`, 添加依赖,添加web支持!\n\n2. 建立包结构 com.ajream.controller\n\n3. 由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:\n\n ```xml\n \n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n false\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n false\n \n \n \n ```\n\n\n\n## 进行开发\n\n### 配置web.xml\n\n注意点:\n\n- 注意web.xml版本问题,要最新版[4.0版]!\n- 注册DispatcherServlet\n- 关联SpringMVC的配置文件:`springmvc-servlet.xml`(先在resources新建springmvc-servlet.xml文件)\n- 启动级别为1\n- 映射路径为 `/` 【不要用`/*`,会404】\n\n\n\n```xml\n\n\n\n \n \n SpringMVC\n org.springframework.web.servlet.DispatcherServlet\n \n \n \n contextConfigLocation\n \n classpath:springmvc-servlet.xml \n \n \n \n 1\n \n \n \n \n SpringMVC\n /\n \n\n\n```\n\n\n\n{%note info flat%}\n\n注意`/` 和 `/*` 的区别:\n\n` / ` 不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类\n\n` /* ` 会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错\n\n{%endnote%}\n\n\n\n\n\n### 添加Spring MVC配置文件\n\n- 让IOC的注解生效\n- 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..\n- MVC的注解驱动\n- 配置视图解析器\n\n在resource目录下添加`springmvc-servlet.xml`配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:\n\n\n\n```xml\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n{%note info flat%}\n\n在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。\n\n{%endnote%}\n\n\n\n### 创建Controller\n\n\n\n编写一个Java控制类: `com.kuang.controller.HelloController` , 注意编码规范\n\n- `@Controller`是为了让Spring IOC容器初始化时自动扫描到;\n- `@RequestMapping`是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;\n- 方法中声明Model类型的参数是为了把Action中的数据带到视图中;\n- 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/**hello**.jsp。\n\n```java\npackage com.kuang.controller;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/HelloController\")\npublic class HelloController {\n \n //真实访问地址 : 项目名/HelloController/hello\n @RequestMapping(\"/hello\")\n public String sayHello(Model model){\n \n //向模型中添加属性msg与值,可以在JSP页面中取出并渲染\n model.addAttribute(\"msg\",\"hello,SpringMVC\");\n \n //web-inf/jsp/hello.jsp\n return \"hello\";\n }\n}\n```\n\n\n\n\n\n### 创建视图层\n\n在WEB-INF/ jsp目录中创建`hello.jsp` , 视图可以直接取出并展示从Controller带回的信息;\n\n可以通过EL表示取出Model中存放的值,或者对象;\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n SpringMVC\n\n\n ${msg}\n\n\n```\n\n\n\n\n\n### 配置Tomcat运行\n\n配置Tomcat , 开启服务器 , 访问对应的请求路径 `http://localhost:8083/HelloController/hello`\n\n![image-20210903225624996](https://gitee.com/ajream/images/raw/master/img/20210903225626_image-20210903225624996.png)\n\n","source":"_posts/SpringMVC/springMVC(三)使用注解开发.md","raw":"---\ntitle: SpringMVC(三)HelloMVC(注解版)\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: SpringMVC(注解版本)的HelloWorld程序\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: b8426075\ndate: 2021-09-03 15:01:20\n---\n\n\n\n\n\n\n\n[项目地址springMVC-03-hello-annotetion](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 新建一个project:`springmvc-03-hello-annotation`, 添加依赖,添加web支持!\n\n2. 建立包结构 com.ajream.controller\n\n3. 由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:\n\n ```xml\n \n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n false\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n false\n \n \n \n ```\n\n\n\n## 进行开发\n\n### 配置web.xml\n\n注意点:\n\n- 注意web.xml版本问题,要最新版[4.0版]!\n- 注册DispatcherServlet\n- 关联SpringMVC的配置文件:`springmvc-servlet.xml`(先在resources新建springmvc-servlet.xml文件)\n- 启动级别为1\n- 映射路径为 `/` 【不要用`/*`,会404】\n\n\n\n```xml\n\n\n\n \n \n SpringMVC\n org.springframework.web.servlet.DispatcherServlet\n \n \n \n contextConfigLocation\n \n classpath:springmvc-servlet.xml \n \n \n \n 1\n \n \n \n \n SpringMVC\n /\n \n\n\n```\n\n\n\n{%note info flat%}\n\n注意`/` 和 `/*` 的区别:\n\n` / ` 不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类\n\n` /* ` 会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错\n\n{%endnote%}\n\n\n\n\n\n### 添加Spring MVC配置文件\n\n- 让IOC的注解生效\n- 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..\n- MVC的注解驱动\n- 配置视图解析器\n\n在resource目录下添加`springmvc-servlet.xml`配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:\n\n\n\n```xml\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n{%note info flat%}\n\n在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。\n\n{%endnote%}\n\n\n\n### 创建Controller\n\n\n\n编写一个Java控制类: `com.kuang.controller.HelloController` , 注意编码规范\n\n- `@Controller`是为了让Spring IOC容器初始化时自动扫描到;\n- `@RequestMapping`是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;\n- 方法中声明Model类型的参数是为了把Action中的数据带到视图中;\n- 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/**hello**.jsp。\n\n```java\npackage com.kuang.controller;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n@Controller\n@RequestMapping(\"/HelloController\")\npublic class HelloController {\n \n //真实访问地址 : 项目名/HelloController/hello\n @RequestMapping(\"/hello\")\n public String sayHello(Model model){\n \n //向模型中添加属性msg与值,可以在JSP页面中取出并渲染\n model.addAttribute(\"msg\",\"hello,SpringMVC\");\n \n //web-inf/jsp/hello.jsp\n return \"hello\";\n }\n}\n```\n\n\n\n\n\n### 创建视图层\n\n在WEB-INF/ jsp目录中创建`hello.jsp` , 视图可以直接取出并展示从Controller带回的信息;\n\n可以通过EL表示取出Model中存放的值,或者对象;\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n SpringMVC\n\n\n ${msg}\n\n\n```\n\n\n\n\n\n### 配置Tomcat运行\n\n配置Tomcat , 开启服务器 , 访问对应的请求路径 `http://localhost:8083/HelloController/hello`\n\n![image-20210903225624996](https://gitee.com/ajream/images/raw/master/img/20210903225626_image-20210903225624996.png)\n\n","slug":"SpringMVC/springMVC(三)使用注解开发","published":1,"updated":"2021-09-05T10:34:03.284Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q5004oakve8g0k1g9x","content":"

项目地址springMVC-03-hello-annotetion

\n

环境配置

    \n
  1. 新建一个project:springmvc-03-hello-annotation, 添加依赖,添加web支持!

    \n
  2. \n
  3. 建立包结构 com.ajream.controller

    \n
  4. \n
  5. 由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <build>
    <resources>
    <resource>
    <directory>src/main/java</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>false</filtering>
    </resource>
    <resource>
    <directory>src/main/resources</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>false</filtering>
    </resource>
    </resources>
    </build>
    \n
  6. \n
\n

进行开发

配置web.xml

注意点:

\n
    \n
  • 注意web.xml版本问题,要最新版[4.0版]!
  • \n
  • 注册DispatcherServlet
  • \n
  • 关联SpringMVC的配置文件:springmvc-servlet.xml(先在resources新建springmvc-servlet.xml文件)
  • \n
  • 启动级别为1
  • \n
  • 映射路径为 / 【不要用/*,会404】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--1.注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--在resources新建springmvc-servlet.xml文件-->
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
\n

注意//* 的区别:

\n

<url-pattern> / </url-pattern> 不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类

\n

<url-pattern> /* </url-pattern> 会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错

\n
\n

添加Spring MVC配置文件

    \n
  • 让IOC的注解生效
  • \n
  • 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..
  • \n
  • MVC的注解驱动
  • \n
  • 配置视图解析器
  • \n
\n

在resource目录下添加springmvc-servlet.xml配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.kuang.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
\n

在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。

\n
\n

创建Controller

编写一个Java控制类: com.kuang.controller.HelloController , 注意编码规范

\n
    \n
  • @Controller是为了让Spring IOC容器初始化时自动扫描到;
  • \n
  • @RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;
  • \n
  • 方法中声明Model类型的参数是为了把Action中的数据带到视图中;
  • \n
  • 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp。
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.kuang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/HelloController")
public class HelloController {

//真实访问地址 : 项目名/HelloController/hello
@RequestMapping("/hello")
public String sayHello(Model model){

//向模型中添加属性msg与值,可以在JSP页面中取出并渲染
model.addAttribute("msg","hello,SpringMVC");

//web-inf/jsp/hello.jsp
return "hello";
}
}
\n

创建视图层

在WEB-INF/ jsp目录中创建hello.jsp , 视图可以直接取出并展示从Controller带回的信息;

\n

可以通过EL表示取出Model中存放的值,或者对象;

\n
1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SpringMVC</title>
</head>
<body>
${msg}
</body>
</html>
\n

配置Tomcat运行

配置Tomcat , 开启服务器 , 访问对应的请求路径 http://localhost:8083/HelloController/hello

\n

\"image-20210903225624996\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

项目地址springMVC-03-hello-annotetion

\n

环境配置

    \n
  1. 新建一个project:springmvc-03-hello-annotation, 添加依赖,添加web支持!

    \n
  2. \n
  3. 建立包结构 com.ajream.controller

    \n
  4. \n
  5. 由于Maven可能存在资源过滤的问题,我们将配置完善,在pom.xml添加:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <build>
    <resources>
    <resource>
    <directory>src/main/java</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>false</filtering>
    </resource>
    <resource>
    <directory>src/main/resources</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    <filtering>false</filtering>
    </resource>
    </resources>
    </build>
    \n
  6. \n
\n

进行开发

配置web.xml

注意点:

\n
    \n
  • 注意web.xml版本问题,要最新版[4.0版]!
  • \n
  • 注册DispatcherServlet
  • \n
  • 关联SpringMVC的配置文件:springmvc-servlet.xml(先在resources新建springmvc-servlet.xml文件)
  • \n
  • 启动级别为1
  • \n
  • 映射路径为 / 【不要用/*,会404】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--1.注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--在resources新建springmvc-servlet.xml文件-->
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
\n

注意//* 的区别:

\n

<url-pattern> / </url-pattern> 不会匹配到.jsp, 只针对我们编写的请求,即:.jsp 不会进入spring的 DispatcherServlet类

\n

<url-pattern> /* </url-pattern> 会匹配 .jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错

\n
\n

添加Spring MVC配置文件

    \n
  • 让IOC的注解生效
  • \n
  • 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..
  • \n
  • MVC的注解驱动
  • \n
  • 配置视图解析器
  • \n
\n

在resource目录下添加springmvc-servlet.xml配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.kuang.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
\n

在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。

\n
\n

创建Controller

编写一个Java控制类: com.kuang.controller.HelloController , 注意编码规范

\n
    \n
  • @Controller是为了让Spring IOC容器初始化时自动扫描到;
  • \n
  • @RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;
  • \n
  • 方法中声明Model类型的参数是为了把Action中的数据带到视图中;
  • \n
  • 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp。
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.kuang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/HelloController")
public class HelloController {

//真实访问地址 : 项目名/HelloController/hello
@RequestMapping("/hello")
public String sayHello(Model model){

//向模型中添加属性msg与值,可以在JSP页面中取出并渲染
model.addAttribute("msg","hello,SpringMVC");

//web-inf/jsp/hello.jsp
return "hello";
}
}
\n

创建视图层

在WEB-INF/ jsp目录中创建hello.jsp , 视图可以直接取出并展示从Controller带回的信息;

\n

可以通过EL表示取出Model中存放的值,或者对象;

\n
1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SpringMVC</title>
</head>
<body>
${msg}
</body>
</html>
\n

配置Tomcat运行

配置Tomcat , 开启服务器 , 访问对应的请求路径 http://localhost:8083/HelloController/hello

\n

\"image-20210903225624996\"

\n"},{"title":"SpringMVC(六)数据处理和乱码问题","description":"对提交的数据进行处理以及乱码问题的解决","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"ecec7d3f","date":"2021-09-04T08:26:20.000Z","_content":"\n\n\n## 获取url提交的数据\n\n\n\n[项目地址springMVC-06-dataProcess](https://codechina.csdn.net/m0_46079750/springmvc/-/tree/master1)\n\n### 通过url提交的数据\n\n**url的参数名与java方法中的参数名一致**(方法名没要求),如下:都是 `name`\n\nurl: http://localhost:8083/hello?name=ajream\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**url的参数名与java方法中的参数名不一致**, 如下,url中为 `username`, java方法中为 `name`\n\nurl: http://localhost:8083/hello?username=ajream\n\n使用注解:`@RequestParam`\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(@RequestParam(\"username\") String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**传入的数据封装成对象**\n\n首先创建一个 `User` 类\n\n```java\npackage com.ajream.pojo;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class User {\n private String name;\n private int id;\n private int age;\n}\n\n```\n\n在 `Controller`中,添加一个参数 `User user`,表示传入的参数将会被封装成一个 `user` 对象\n\n```java\n@RequestMapping(\"/t2\")\npublic String test2(User user, Model model){\n System.out.println(user);\n model.addAttribute(\"msg\", user);\n return \"hello\";\n}\n```\n\n\n\n测试运行,访问 `http://localhost:8083/t2?name=ajream&age=10&id=10001` :\n\n![image-20210904190448500](https://gitee.com/ajream/images/raw/master/img/20210904190452_image-20210904190448500.png)\n\n\n\n> 注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下\n>\n> ![image-20210904190750826](https://gitee.com/ajream/images/raw/master/img/20210904190752_image-20210904190750826.png)\n\n\n\n\n\n\n\n## 表单提交的数据乱码\n\n### POST提交数据乱码\n\n创建表单提交页面 `web/form.jsp`\n\n![image-20210904195836766](https://gitee.com/ajream/images/raw/master/img/20210904195839_image-20210904195836766.png)\n\n添加代码:\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title\n\n\n
\n \n \n
\n\n\n\n\n```\n\n\n\n编写后台处理类\n\n```java\npackage com.ajream.encoding;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.PostMapping;\n\n@Controller\npublic class EncodingController {\n @PostMapping(\"/e/t1\")\n public String test1(String name, Model model){\n model.addAttribute(\"msg\", name);\n System.out.println(name);\n return \"test\";\n }\n}\n\n```\n\n运行测试,先访问表单页 `http:localhost:8083/form.jsp`, 提交后自动跳转到 `localhost:8083/e/t1`页面\n\n传入中文数据,返回乱码:\n\n![image-20210904200350867](https://gitee.com/ajream/images/raw/master/img/20210904200352_image-20210904200350867.png)\n\n后台也是:\n\n![image-20210904200522875](https://gitee.com/ajream/images/raw/master/img/20210904200524_image-20210904200522875.png)\n\n\n\n\n\n{%folding blue, 提交表单后出现bug%}\n\n提交表单出现404页面\n\n![image-20210904200902942](https://gitee.com/ajream/images/raw/master/img/20210904200904_image-20210904200902942.png)\n\n\n\n原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决\n\n![image-20210904201311953](https://gitee.com/ajream/images/raw/master/img/20210904201313_image-20210904201311953.png)\n\n\n\n{%endfolding%}\n\n\n\n\n\n### 乱码问题解决\n\n\n\n在 `web.xml` 添加过滤器(这里用的是springMVC提供的)\n\n\n\n```xml\n\n encoding\n org.springframework.web.filter.CharacterEncodingFilter\n \n encoding\n utf-8\n \n\n\n encoding\n /* \n\n```\n\n{%note info flat%}\n\n注意`url-pattern`是 \"/*\", 不是 \"/\"\n\n{%endnote%}\n\n\n\n重新运行,前端、后台都显示正常:\n\n| 前端 | 后台 |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210904202053181](https://gitee.com/ajream/images/raw/master/img/20210904202054_image-20210904202053181.png) | \"image-20210904202144232\" |\n\n\n\n\n\n{%folding blue, 极端情况对get请求支持不好%}\n\n有些极端情况下.这个过滤器对get的支持不好 .\n\n处理方法 :\n\n修改tomcat配置文件`server.xml`(设置编码)\n\n```xml\n\n```\n\n自定义过滤器:\n\n```java\npackage com.kuang.filter;\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Map;\n/**\n * 解决get和post请求 全部乱码的过滤器\n */\npublic class GenericEncodingFilter implements Filter {\n @Override\n public void destroy() {\n }\n @Override\n public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\n //处理response的字符编码\n HttpServletResponse myResponse=(HttpServletResponse) response;\n myResponse.setContentType(\"text/html;charset=UTF-8\");\n // 转型为与协议相关对象\n HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n // 对request包装增强\n HttpServletRequest myrequest = new MyRequest(httpServletRequest);\n chain.doFilter(myrequest, response);\n }\n @Override\n public void init(FilterConfig filterConfig) throws ServletException {\n }\n}\n//自定义request对象,HttpServletRequest的包装类\nclass MyRequest extends HttpServletRequestWrapper {\n private HttpServletRequest request;\n //是否编码的标记\n private boolean hasEncode;\n //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰\n public MyRequest(HttpServletRequest request) {\n super(request);// super必须写\n this.request = request;\n }\n // 对需要增强方法 进行覆盖\n @Override\n public Map getParameterMap() {\n // 先获得请求方式\n String method = request.getMethod();\n if (method.equalsIgnoreCase(\"post\")) {\n // post请求\n try {\n // 处理post乱码\n request.setCharacterEncoding(\"utf-8\");\n return request.getParameterMap();\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n } else if (method.equalsIgnoreCase(\"get\")) {\n // get请求\n Map parameterMap = request.getParameterMap();\n if (!hasEncode) { // 确保get手动编码逻辑只运行一次\n for (String parameterName : parameterMap.keySet()) {\n String[] values = parameterMap.get(parameterName);\n if (values != null) {\n for (int i = 0; i < values.length; i++) {\n try {\n // 处理get乱码\n values[i] = new String(values[i]\n .getBytes(\"ISO-8859-1\"), \"utf-8\");\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n }\n }\n }\n hasEncode = true;\n }\n return parameterMap;\n }\n return super.getParameterMap();\n }\n //取一个值\n @Override\n public String getParameter(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n if (values == null) {\n return null;\n }\n return values[0]; // 取回参数的第一个值\n }\n //取所有值\n @Override\n public String[] getParameterValues(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n return values;\n }\n}\n```\n\n\n\n在web.xml添加自定义过滤器即可\n\n```xml\n\n encoding\n 【修改为自定义过滤器】\n \n encoding\n utf-8\n \n\n\n encoding\n /* \n\n```\n\n\n\n{%note flat%}\n\n乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n\n\n","source":"_posts/SpringMVC/springMVC(六)数据处理.md","raw":"---\ntitle: SpringMVC(六)数据处理和乱码问题\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: 对提交的数据进行处理以及乱码问题的解决\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: ecec7d3f\ndate: 2021-09-04 16:26:20\n---\n\n\n\n## 获取url提交的数据\n\n\n\n[项目地址springMVC-06-dataProcess](https://codechina.csdn.net/m0_46079750/springmvc/-/tree/master1)\n\n### 通过url提交的数据\n\n**url的参数名与java方法中的参数名一致**(方法名没要求),如下:都是 `name`\n\nurl: http://localhost:8083/hello?name=ajream\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**url的参数名与java方法中的参数名不一致**, 如下,url中为 `username`, java方法中为 `name`\n\nurl: http://localhost:8083/hello?username=ajream\n\n使用注解:`@RequestParam`\n\n```java\n@RequestMapping(\"/hello\")\npublic String hello(@RequestParam(\"username\") String name){\n System.out.println(name);\n return \"hello\";\n}\n```\n\n\n\n**传入的数据封装成对象**\n\n首先创建一个 `User` 类\n\n```java\npackage com.ajream.pojo;\n\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class User {\n private String name;\n private int id;\n private int age;\n}\n\n```\n\n在 `Controller`中,添加一个参数 `User user`,表示传入的参数将会被封装成一个 `user` 对象\n\n```java\n@RequestMapping(\"/t2\")\npublic String test2(User user, Model model){\n System.out.println(user);\n model.addAttribute(\"msg\", user);\n return \"hello\";\n}\n```\n\n\n\n测试运行,访问 `http://localhost:8083/t2?name=ajream&age=10&id=10001` :\n\n![image-20210904190448500](https://gitee.com/ajream/images/raw/master/img/20210904190452_image-20210904190448500.png)\n\n\n\n> 注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下\n>\n> ![image-20210904190750826](https://gitee.com/ajream/images/raw/master/img/20210904190752_image-20210904190750826.png)\n\n\n\n\n\n\n\n## 表单提交的数据乱码\n\n### POST提交数据乱码\n\n创建表单提交页面 `web/form.jsp`\n\n![image-20210904195836766](https://gitee.com/ajream/images/raw/master/img/20210904195839_image-20210904195836766.png)\n\n添加代码:\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title\n\n\n
\n \n \n
\n\n\n\n\n```\n\n\n\n编写后台处理类\n\n```java\npackage com.ajream.encoding;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.PostMapping;\n\n@Controller\npublic class EncodingController {\n @PostMapping(\"/e/t1\")\n public String test1(String name, Model model){\n model.addAttribute(\"msg\", name);\n System.out.println(name);\n return \"test\";\n }\n}\n\n```\n\n运行测试,先访问表单页 `http:localhost:8083/form.jsp`, 提交后自动跳转到 `localhost:8083/e/t1`页面\n\n传入中文数据,返回乱码:\n\n![image-20210904200350867](https://gitee.com/ajream/images/raw/master/img/20210904200352_image-20210904200350867.png)\n\n后台也是:\n\n![image-20210904200522875](https://gitee.com/ajream/images/raw/master/img/20210904200524_image-20210904200522875.png)\n\n\n\n\n\n{%folding blue, 提交表单后出现bug%}\n\n提交表单出现404页面\n\n![image-20210904200902942](https://gitee.com/ajream/images/raw/master/img/20210904200904_image-20210904200902942.png)\n\n\n\n原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决\n\n![image-20210904201311953](https://gitee.com/ajream/images/raw/master/img/20210904201313_image-20210904201311953.png)\n\n\n\n{%endfolding%}\n\n\n\n\n\n### 乱码问题解决\n\n\n\n在 `web.xml` 添加过滤器(这里用的是springMVC提供的)\n\n\n\n```xml\n\n encoding\n org.springframework.web.filter.CharacterEncodingFilter\n \n encoding\n utf-8\n \n\n\n encoding\n /* \n\n```\n\n{%note info flat%}\n\n注意`url-pattern`是 \"/*\", 不是 \"/\"\n\n{%endnote%}\n\n\n\n重新运行,前端、后台都显示正常:\n\n| 前端 | 后台 |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210904202053181](https://gitee.com/ajream/images/raw/master/img/20210904202054_image-20210904202053181.png) | \"image-20210904202144232\" |\n\n\n\n\n\n{%folding blue, 极端情况对get请求支持不好%}\n\n有些极端情况下.这个过滤器对get的支持不好 .\n\n处理方法 :\n\n修改tomcat配置文件`server.xml`(设置编码)\n\n```xml\n\n```\n\n自定义过滤器:\n\n```java\npackage com.kuang.filter;\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Map;\n/**\n * 解决get和post请求 全部乱码的过滤器\n */\npublic class GenericEncodingFilter implements Filter {\n @Override\n public void destroy() {\n }\n @Override\n public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\n //处理response的字符编码\n HttpServletResponse myResponse=(HttpServletResponse) response;\n myResponse.setContentType(\"text/html;charset=UTF-8\");\n // 转型为与协议相关对象\n HttpServletRequest httpServletRequest = (HttpServletRequest) request;\n // 对request包装增强\n HttpServletRequest myrequest = new MyRequest(httpServletRequest);\n chain.doFilter(myrequest, response);\n }\n @Override\n public void init(FilterConfig filterConfig) throws ServletException {\n }\n}\n//自定义request对象,HttpServletRequest的包装类\nclass MyRequest extends HttpServletRequestWrapper {\n private HttpServletRequest request;\n //是否编码的标记\n private boolean hasEncode;\n //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰\n public MyRequest(HttpServletRequest request) {\n super(request);// super必须写\n this.request = request;\n }\n // 对需要增强方法 进行覆盖\n @Override\n public Map getParameterMap() {\n // 先获得请求方式\n String method = request.getMethod();\n if (method.equalsIgnoreCase(\"post\")) {\n // post请求\n try {\n // 处理post乱码\n request.setCharacterEncoding(\"utf-8\");\n return request.getParameterMap();\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n } else if (method.equalsIgnoreCase(\"get\")) {\n // get请求\n Map parameterMap = request.getParameterMap();\n if (!hasEncode) { // 确保get手动编码逻辑只运行一次\n for (String parameterName : parameterMap.keySet()) {\n String[] values = parameterMap.get(parameterName);\n if (values != null) {\n for (int i = 0; i < values.length; i++) {\n try {\n // 处理get乱码\n values[i] = new String(values[i]\n .getBytes(\"ISO-8859-1\"), \"utf-8\");\n } catch (UnsupportedEncodingException e) {\n e.printStackTrace();\n }\n }\n }\n }\n hasEncode = true;\n }\n return parameterMap;\n }\n return super.getParameterMap();\n }\n //取一个值\n @Override\n public String getParameter(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n if (values == null) {\n return null;\n }\n return values[0]; // 取回参数的第一个值\n }\n //取所有值\n @Override\n public String[] getParameterValues(String name) {\n Map parameterMap = getParameterMap();\n String[] values = parameterMap.get(name);\n return values;\n }\n}\n```\n\n\n\n在web.xml添加自定义过滤器即可\n\n```xml\n\n encoding\n 【修改为自定义过滤器】\n \n encoding\n utf-8\n \n\n\n encoding\n /* \n\n```\n\n\n\n{%note flat%}\n\n乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!\n\n{%endnote%}\n\n{%endfolding%}\n\n\n\n\n\n\n\n","slug":"SpringMVC/springMVC(六)数据处理","published":1,"updated":"2021-09-05T10:34:16.597Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q6004rakve7m1j1plt","content":"

获取url提交的数据

项目地址springMVC-06-dataProcess

\n

通过url提交的数据

url的参数名与java方法中的参数名一致(方法名没要求),如下:都是 name

\n

url: http://localhost:8083/hello?name=ajream

\n
1
2
3
4
5
@RequestMapping("/hello")
public String hello(String name){
System.out.println(name);
return "hello";
}
\n

url的参数名与java方法中的参数名不一致, 如下,url中为 username, java方法中为 name

\n

url: http://localhost:8083/hello?username=ajream

\n

使用注解:@RequestParam

\n
1
2
3
4
5
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
System.out.println(name);
return "hello";
}
\n

传入的数据封装成对象

\n

首先创建一个 User

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int id;
private int age;
}

\n

Controller中,添加一个参数 User user,表示传入的参数将会被封装成一个 user 对象

\n
1
2
3
4
5
6
@RequestMapping("/t2")
public String test2(User user, Model model){
System.out.println(user);
model.addAttribute("msg", user);
return "hello";
}
\n

测试运行,访问 http://localhost:8083/t2?name=ajream&age=10&id=10001

\n

\"image-20210904190448500\"

\n
\n

注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下

\n

\"image-20210904190750826\"

\n
\n

表单提交的数据乱码

POST提交数据乱码

创建表单提交页面 web/form.jsp

\n

\"image-20210904195836766\"

\n

添加代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/e/t1" method="post">
<input type="text" name="name">
<input type="submit">
</form>

</body>
</html>

\n

编写后台处理类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.encoding;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class EncodingController {
@PostMapping("/e/t1")
public String test1(String name, Model model){
model.addAttribute("msg", name);
System.out.println(name);
return "test";
}
}

\n

运行测试,先访问表单页 http:localhost:8083/form.jsp, 提交后自动跳转到 localhost:8083/e/t1页面

\n

传入中文数据,返回乱码:

\n

\"image-20210904200350867\"

\n

后台也是:

\n

\"image-20210904200522875\"

\n
提交表单后出现bug \n
\n

提交表单出现404页面

\"image-20210904200902942\"

原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决

\"image-20210904201311953\"

\n
\n
\n

乱码问题解决

web.xml 添加过滤器(这里用的是springMVC提供的)

\n
1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern> <!--注意是 "/*", 不是 "/" -->
</filter-mapping>
\n

注意url-pattern是 “/*”, 不是 “/“

\n
\n

重新运行,前端、后台都显示正常:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
前端后台
\"image-20210904202053181\"\"image-20210904202144232\"
\n
\n
极端情况对get请求支持不好 \n
\n

有些极端情况下.这个过滤器对get的支持不好 .

处理方法 :

修改tomcat配置文件server.xml(设置编码)

1
2
3
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

自定义过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package com.kuang.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}
// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
//取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}
//取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}

在web.xml添加自定义过滤器即可

1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>encoding</filter-name>
<filter-class>【修改为自定义过滤器】</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern> <!--注意是 "/*", 不是 "/" -->
</filter-mapping>

乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!

\n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

获取url提交的数据

项目地址springMVC-06-dataProcess

\n

通过url提交的数据

url的参数名与java方法中的参数名一致(方法名没要求),如下:都是 name

\n

url: http://localhost:8083/hello?name=ajream

\n
1
2
3
4
5
@RequestMapping("/hello")
public String hello(String name){
System.out.println(name);
return "hello";
}
\n

url的参数名与java方法中的参数名不一致, 如下,url中为 username, java方法中为 name

\n

url: http://localhost:8083/hello?username=ajream

\n

使用注解:@RequestParam

\n
1
2
3
4
5
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
System.out.println(name);
return "hello";
}
\n

传入的数据封装成对象

\n

首先创建一个 User

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int id;
private int age;
}

\n

Controller中,添加一个参数 User user,表示传入的参数将会被封装成一个 user 对象

\n
1
2
3
4
5
6
@RequestMapping("/t2")
public String test2(User user, Model model){
System.out.println(user);
model.addAttribute("msg", user);
return "hello";
}
\n

测试运行,访问 http://localhost:8083/t2?name=ajream&age=10&id=10001

\n

\"image-20210904190448500\"

\n
\n

注意传入的参数必须要与类中的属性名一一对应,否则就获取不到数据,如下

\n

\"image-20210904190750826\"

\n
\n

表单提交的数据乱码

POST提交数据乱码

创建表单提交页面 web/form.jsp

\n

\"image-20210904195836766\"

\n

添加代码:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/e/t1" method="post">
<input type="text" name="name">
<input type="submit">
</form>

</body>
</html>

\n

编写后台处理类

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.ajream.encoding;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class EncodingController {
@PostMapping("/e/t1")
public String test1(String name, Model model){
model.addAttribute("msg", name);
System.out.println(name);
return "test";
}
}

\n

运行测试,先访问表单页 http:localhost:8083/form.jsp, 提交后自动跳转到 localhost:8083/e/t1页面

\n

传入中文数据,返回乱码:

\n

\"image-20210904200350867\"

\n

后台也是:

\n

\"image-20210904200522875\"

\n
提交表单后出现bug \n
\n

提交表单出现404页面

\"image-20210904200902942\"

原因是我这是在上一个的项目中进行修改的,我添加了新的包,但没有在 springMVC配置文件中添加对这个包扫描的支持,导致没有注解支持,重新添加后问题解决

\"image-20210904201311953\"

\n
\n
\n

乱码问题解决

web.xml 添加过滤器(这里用的是springMVC提供的)

\n
1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern> <!--注意是 "/*", 不是 "/" -->
</filter-mapping>
\n

注意url-pattern是 “/*”, 不是 “/“

\n
\n

重新运行,前端、后台都显示正常:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
前端后台
\"image-20210904202053181\"\"image-20210904202144232\"
\n
\n
极端情况对get请求支持不好 \n
\n

有些极端情况下.这个过滤器对get的支持不好 .

处理方法 :

修改tomcat配置文件server.xml(设置编码)

1
2
3
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

自定义过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package com.kuang.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}
// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
//取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}
//取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}

在web.xml添加自定义过滤器即可

1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>encoding</filter-name>
<filter-class>【修改为自定义过滤器】</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern> <!--注意是 "/*", 不是 "/" -->
</filter-mapping>

乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!

\n
\n
\n"},{"title":"SpringMVC(二)HelloMVC","description":"SpringMVC的HelloWorld程序","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"a4682555","date":"2021-09-03T02:31:26.000Z","_content":"\n\n\n\n\n[项目地址springMVC-02-hellomvc](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 创建一个普通maven项目 `springmvc-02-hellomvc`\n\n2. 导入依赖\n\n ```xml\n \n \n org.springframework\n spring-webmvc\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.13\n \n \n javax.servlet\n servlet-api\n 2.5\n \n \n javax.servlet.jsp\n jsp-api\n 2.2\n \n \n javax.servlet\n jstl\n 1.2\n \n \n ```\n \n3. 启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)\n\n## 进行开发\n\n### web.xml配置\n\n在【/web/WEB-INF/web.xml】注册 `DispatcherServlet`\n\n```xml\n\n\n\n\n \n springmvc\n org.springframework.web.servlet.DispatcherServlet\n\n\n \n contextConfigLocation\n classpath:springmvc-servlet.xml\n \n\n\n 1\n \n\n\n\n\n \n springmvc\n /\n \n\n```\n\n\n\n### 添加Spring MVC配置文件\n\n\n\n在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:`[servlet-name]-servlet.xml`,在本项目中为 `springmvc-servlet.xml`\n\n添加spring头文件\n\n```xml\n\n\n\n\n```\n\n添加处理映射器\n\n```xml\n\n```\n\n添加处理适配器\n\n```xml\n\n```\n\n添加视图解析器\n\n```xml\n\n\n \n \n \n \n\n```\n\n\n\n### 添加Controller类\n\n在包【com.ajream.controller】编写`HelloController`类,用于具体操作业务\n\n```java\npackage com.ajream.controller;\n\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class HelloController implements Controller {\n// 重写handleRequest方法\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n// 创建模型和视图\n ModelAndView mv = new ModelAndView();\n// 添加对象到模型和视图中\n mv.addObject(\"msg\",\"Hello SpringMVC\");\n// 指定哪个模型视图\n mv.setViewName(\"hello\"); //自动拼接成路径: /WEB-INF/jsp/hello.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后将 `HelloController`类交给SpringIOC,注册到 beans 中\n\n```xml\n\n```\n\n\n\n### 创建视图层\n\n创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title\n\n\n ${msg}\n\n\n\n```\n\n\n\n### 运行Tomcat/访问\n\n配置Tomcat,运行,访问 `http://localhost:8083/hello`\n\n![image-20210903225933025](https://gitee.com/ajream/images/raw/master/img/20210903225934_image-20210903225933025.png)\n\n\n\n\n\n\n\n## 404解决办法\n\n1. 查看控制台输出,看一下是不是缺少了什么jar包。\n\n2. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!\n\n {%folding blue, 点击查看解决办法%}\n\n \n\n 点击工程结构\n\n ![image-20210903224853189](https://gitee.com/ajream/images/raw/master/img/20210903224857_image-20210903224853189.png)\n\n \n\n 选择Artifacts,及自己的project,创建lib文件夹,并导入包\n\n ![image-20210903225354211](https://gitee.com/ajream/images/raw/master/img/20210903225355_image-20210903225354211.png)\n\n {%endfolding%}\n\n3. 重启Tomcat 即可解决!\n\n\n\n","source":"_posts/SpringMVC/springMVC(二)HelloMVC.md","raw":"---\ntitle: SpringMVC(二)HelloMVC\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: SpringMVC的HelloWorld程序\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: a4682555\ndate: 2021-09-03 10:31:26\n---\n\n\n\n\n\n[项目地址springMVC-02-hellomvc](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 环境配置\n\n1. 创建一个普通maven项目 `springmvc-02-hellomvc`\n\n2. 导入依赖\n\n ```xml\n \n \n org.springframework\n spring-webmvc\n 5.2.9.RELEASE\n \n \n junit\n junit\n 4.13\n \n \n javax.servlet\n servlet-api\n 2.5\n \n \n javax.servlet.jsp\n jsp-api\n 2.2\n \n \n javax.servlet\n jstl\n 1.2\n \n \n ```\n \n3. 启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)\n\n## 进行开发\n\n### web.xml配置\n\n在【/web/WEB-INF/web.xml】注册 `DispatcherServlet`\n\n```xml\n\n\n\n\n \n springmvc\n org.springframework.web.servlet.DispatcherServlet\n\n\n \n contextConfigLocation\n classpath:springmvc-servlet.xml\n \n\n\n 1\n \n\n\n\n\n \n springmvc\n /\n \n\n```\n\n\n\n### 添加Spring MVC配置文件\n\n\n\n在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:`[servlet-name]-servlet.xml`,在本项目中为 `springmvc-servlet.xml`\n\n添加spring头文件\n\n```xml\n\n\n\n\n```\n\n添加处理映射器\n\n```xml\n\n```\n\n添加处理适配器\n\n```xml\n\n```\n\n添加视图解析器\n\n```xml\n\n\n \n \n \n \n\n```\n\n\n\n### 添加Controller类\n\n在包【com.ajream.controller】编写`HelloController`类,用于具体操作业务\n\n```java\npackage com.ajream.controller;\n\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class HelloController implements Controller {\n// 重写handleRequest方法\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n// 创建模型和视图\n ModelAndView mv = new ModelAndView();\n// 添加对象到模型和视图中\n mv.addObject(\"msg\",\"Hello SpringMVC\");\n// 指定哪个模型视图\n mv.setViewName(\"hello\"); //自动拼接成路径: /WEB-INF/jsp/hello.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后将 `HelloController`类交给SpringIOC,注册到 beans 中\n\n```xml\n\n```\n\n\n\n### 创建视图层\n\n创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容\n\n```html\n<%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n\n\n Title\n\n\n ${msg}\n\n\n\n```\n\n\n\n### 运行Tomcat/访问\n\n配置Tomcat,运行,访问 `http://localhost:8083/hello`\n\n![image-20210903225933025](https://gitee.com/ajream/images/raw/master/img/20210903225934_image-20210903225933025.png)\n\n\n\n\n\n\n\n## 404解决办法\n\n1. 查看控制台输出,看一下是不是缺少了什么jar包。\n\n2. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!\n\n {%folding blue, 点击查看解决办法%}\n\n \n\n 点击工程结构\n\n ![image-20210903224853189](https://gitee.com/ajream/images/raw/master/img/20210903224857_image-20210903224853189.png)\n\n \n\n 选择Artifacts,及自己的project,创建lib文件夹,并导入包\n\n ![image-20210903225354211](https://gitee.com/ajream/images/raw/master/img/20210903225355_image-20210903225354211.png)\n\n {%endfolding%}\n\n3. 重启Tomcat 即可解决!\n\n\n\n","slug":"SpringMVC/springMVC(二)HelloMVC","published":1,"updated":"2021-09-05T10:33:58.666Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q7004takvecc6ph7mb","content":"

项目地址springMVC-02-hellomvc

\n

环境配置

    \n
  1. 创建一个普通maven项目 springmvc-02-hellomvc

    \n
  2. \n
  3. 导入依赖

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    </dependency>
    </dependencies>
    \n
  4. \n
  5. 启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)

    \n
  6. \n
\n

进行开发

web.xml配置

在【/web/WEB-INF/web.xml】注册 DispatcherServlet

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!-- 注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!-- 关联一个springmvc配置文件,文件名:[servlet-name]-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动级别-->
<load-on-startup>1</load-on-startup>
</servlet>

<!-- 匹配请求 -->
<!-- "/" : 匹配所有请求,除了 ".jsp"-->
<!-- "/*": 匹配 所有请求,包括 ".jsp"-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
\n

添加Spring MVC配置文件

在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:[servlet-name]-servlet.xml,在本项目中为 springmvc-servlet.xml

\n

添加spring头文件

\n
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>
\n

添加处理映射器

\n
1
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
\n

添加处理适配器

\n
1
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
\n

添加视图解析器

\n
1
2
3
4
5
6
7
<!--视图解析器,Dispatcherservlet给他的ModelAndView-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
\n

添加Controller类

在包【com.ajream.controller】编写HelloController类,用于具体操作业务

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ajream.controller;


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//实现Controller接口
public class HelloController implements Controller {
// 重写handleRequest方法
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// 创建模型和视图
ModelAndView mv = new ModelAndView();
// 添加对象到模型和视图中
mv.addObject("msg","Hello SpringMVC");
// 指定哪个模型视图
mv.setViewName("hello"); //自动拼接成路径: /WEB-INF/jsp/hello.jsp
return mv;
}
}

\n

然后将 HelloController类交给SpringIOC,注册到 beans 中

\n
1
<bean id="/hello" class="com.ajream.controller.HelloController"/>
\n

创建视图层

创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容

\n
1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>

\n

运行Tomcat/访问

配置Tomcat,运行,访问 http://localhost:8083/hello

\n

\"image-20210903225933025\"

\n

404解决办法

    \n
  1. 查看控制台输出,看一下是不是缺少了什么jar包。

    \n
  2. \n
  3. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!

    \n
    点击查看解决办法 \n
    \n

    点击工程结构

    \"image-20210903224853189\"

    选择Artifacts,及自己的project,创建lib文件夹,并导入包

    \"image-20210903225354211\"

    \n
    \n
    \n
  4. \n
  5. 重启Tomcat 即可解决!

    \n
  6. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

项目地址springMVC-02-hellomvc

\n

环境配置

    \n
  1. 创建一个普通maven项目 springmvc-02-hellomvc

    \n
  2. \n
  3. 导入依赖

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.9.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    </dependency>
    </dependencies>
    \n
  4. \n
  5. 启动web项目(看上一篇文章的 “普通maven项目转为web项目”部分)

    \n
  6. \n
\n

进行开发

web.xml配置

在【/web/WEB-INF/web.xml】注册 DispatcherServlet

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!-- 注册DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!-- 关联一个springmvc配置文件,文件名:[servlet-name]-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动级别-->
<load-on-startup>1</load-on-startup>
</servlet>

<!-- 匹配请求 -->
<!-- "/" : 匹配所有请求,除了 ".jsp"-->
<!-- "/*": 匹配 所有请求,包括 ".jsp"-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
\n

添加Spring MVC配置文件

在【resources】下编写SpringMvc的配置文件,名称要按照官方的格式:[servlet-name]-servlet.xml,在本项目中为 springmvc-servlet.xml

\n

添加spring头文件

\n
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>
\n

添加处理映射器

\n
1
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
\n

添加处理适配器

\n
1
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
\n

添加视图解析器

\n
1
2
3
4
5
6
7
<!--视图解析器,Dispatcherservlet给他的ModelAndView-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>
\n

添加Controller类

在包【com.ajream.controller】编写HelloController类,用于具体操作业务

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ajream.controller;


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//实现Controller接口
public class HelloController implements Controller {
// 重写handleRequest方法
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// 创建模型和视图
ModelAndView mv = new ModelAndView();
// 添加对象到模型和视图中
mv.addObject("msg","Hello SpringMVC");
// 指定哪个模型视图
mv.setViewName("hello"); //自动拼接成路径: /WEB-INF/jsp/hello.jsp
return mv;
}
}

\n

然后将 HelloController类交给SpringIOC,注册到 beans 中

\n
1
<bean id="/hello" class="com.ajream.controller.HelloController"/>
\n

创建视图层

创建【/web/WEB-INF/jsp/hello.xml】视图文件,即请求后要跳转到的文件,添加以下内容

\n
1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>

\n

运行Tomcat/访问

配置Tomcat,运行,访问 http://localhost:8083/hello

\n

\"image-20210903225933025\"

\n

404解决办法

    \n
  1. 查看控制台输出,看一下是不是缺少了什么jar包。

    \n
  2. \n
  3. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!

    \n
    点击查看解决办法 \n
    \n

    点击工程结构

    \"image-20210903224853189\"

    选择Artifacts,及自己的project,创建lib文件夹,并导入包

    \"image-20210903225354211\"

    \n
    \n
    \n
  4. \n
  5. 重启Tomcat 即可解决!

    \n
  6. \n
\n"},{"title":"SpringMVC(五)请求结果的跳转方式","description":"请求结果的跳转方式:转发、重定向","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"1eb33081","date":"2021-09-04T07:26:20.000Z","_content":"\n\n\n[项目地址springMVC-05-resultgo](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 理解概念\n\n转发:将某一信息转发到当前访问路径的页面\n\n重定向:从当前页面跳转到另一页面\n\n\n\n> 转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。\n>\n> 重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。\n\n## ModelAndView\n\n设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。\n\n比如:\n\n```java\npublic ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n}\n```\n\n\n\n视图解析器:\n\n```xml\n\n\n \n \n \n \n\n```\n\n\n\n解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:`\"/WEB-INF/jsp/\"`+ `\"test1\"` + `\".jsp\"` --> `\"/WEB-INF/jsp/test.jsp\"`\n\n\n\n\n\n## ServletAPI\n\n通过设置ServletAPI(很少用了) , 不需要视图解析器\n\n1. 通过HttpServletResponse进行输出\n2. 通过HttpServletResponse实现重定向\n3. 通过HttpServletResponse实现转发\n\n\n\n```java\n@Controller\npublic class ResultGo {\n @RequestMapping(\"/result/t1\")\n public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n rsp.getWriter().println(\"Hello,Spring BY servlet API\");\n }\n @RequestMapping(\"/result/t2\")\n public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n// 重定向\n rsp.sendRedirect(\"/index.jsp\");\n }\n @RequestMapping(\"/result/t3\")\n public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {\n //转发\n req.setAttribute(\"msg\",\"/result/t3\");\n req.getRequestDispatcher(\"/WEB-INF/jsp/test.jsp\").forward(req,rsp);\n }\n}\n```\n\n\n\n\n\n## SpringMVC\n\n### 通过SpringMVC来实现转发和重定向-无视图解析器\n\n测试前,需要将视图解析器注释掉\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC {\n @RequestMapping(\"/rsm/t1\")\n public String test1(){\n// 转发一\n return \"/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t2\")\n public String test2(){\n// 转发二\n return \"forward:/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t3\")\n public String test3(){\n// 重定向\n return \"redirect:/index.jsp\";\n\n }\n}\n\n```\n\n转发:\n\n![image-20210904160714983](https://gitee.com/ajream/images/raw/master/img/20210904160718_image-20210904160714983.png)\n\n\n\n\n\n重定向:访问 `http://localhost:8083/rsm/t3` 后重定向到 `http://localhost:8083/index.jsp`\n\n![image-20210904160851220](https://gitee.com/ajream/images/raw/master/img/20210904160852_image-20210904160851220.png)\n\n\n\n\n\n### 使用视图解析器\n\n添加视图解析器\n\n```xml\n\n \n \n\n```\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC2 {\n @RequestMapping(\"/rsm2/t1\")\n public String test1(){\n //转发\n return \"test\"; //通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n }\n @RequestMapping(\"/rsm2/t2\")\n public String test2(){\n //重定向\n return \"redirect:/index.jsp\"; // 使用重定向关键字\"redirect\"后不会经过视图解析器\n //return \"redirect:hello.do\"; //hello.do为另一个请求/\n }\n}\n```\n\n\n\n> 说明:\n>\n> `return \"test\"` 通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n>\n> `return \"redirect:/index.jsp\"` 使用重定向关键字\"redirect\"后不会经过视图解析器\n\n","source":"_posts/SpringMVC/springMVC(五)结果跳转方式.md","raw":"---\ntitle: SpringMVC(五)请求结果的跳转方式\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: 请求结果的跳转方式:转发、重定向\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: 1eb33081\ndate: 2021-09-04 15:26:20\n---\n\n\n\n[项目地址springMVC-05-resultgo](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 理解概念\n\n转发:将某一信息转发到当前访问路径的页面\n\n重定向:从当前页面跳转到另一页面\n\n\n\n> 转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。\n>\n> 重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。\n\n## ModelAndView\n\n设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。\n\n比如:\n\n```java\npublic ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n}\n```\n\n\n\n视图解析器:\n\n```xml\n\n\n \n \n \n \n\n```\n\n\n\n解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:`\"/WEB-INF/jsp/\"`+ `\"test1\"` + `\".jsp\"` --> `\"/WEB-INF/jsp/test.jsp\"`\n\n\n\n\n\n## ServletAPI\n\n通过设置ServletAPI(很少用了) , 不需要视图解析器\n\n1. 通过HttpServletResponse进行输出\n2. 通过HttpServletResponse实现重定向\n3. 通过HttpServletResponse实现转发\n\n\n\n```java\n@Controller\npublic class ResultGo {\n @RequestMapping(\"/result/t1\")\n public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n rsp.getWriter().println(\"Hello,Spring BY servlet API\");\n }\n @RequestMapping(\"/result/t2\")\n public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {\n// 重定向\n rsp.sendRedirect(\"/index.jsp\");\n }\n @RequestMapping(\"/result/t3\")\n public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {\n //转发\n req.setAttribute(\"msg\",\"/result/t3\");\n req.getRequestDispatcher(\"/WEB-INF/jsp/test.jsp\").forward(req,rsp);\n }\n}\n```\n\n\n\n\n\n## SpringMVC\n\n### 通过SpringMVC来实现转发和重定向-无视图解析器\n\n测试前,需要将视图解析器注释掉\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC {\n @RequestMapping(\"/rsm/t1\")\n public String test1(){\n// 转发一\n return \"/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t2\")\n public String test2(){\n// 转发二\n return \"forward:/index.jsp\";\n }\n\n @RequestMapping(\"/rsm/t3\")\n public String test3(){\n// 重定向\n return \"redirect:/index.jsp\";\n\n }\n}\n\n```\n\n转发:\n\n![image-20210904160714983](https://gitee.com/ajream/images/raw/master/img/20210904160718_image-20210904160714983.png)\n\n\n\n\n\n重定向:访问 `http://localhost:8083/rsm/t3` 后重定向到 `http://localhost:8083/index.jsp`\n\n![image-20210904160851220](https://gitee.com/ajream/images/raw/master/img/20210904160852_image-20210904160851220.png)\n\n\n\n\n\n### 使用视图解析器\n\n添加视图解析器\n\n```xml\n\n \n \n\n```\n\n\n\n```java\n@Controller\npublic class ResultSpringMVC2 {\n @RequestMapping(\"/rsm2/t1\")\n public String test1(){\n //转发\n return \"test\"; //通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n }\n @RequestMapping(\"/rsm2/t2\")\n public String test2(){\n //重定向\n return \"redirect:/index.jsp\"; // 使用重定向关键字\"redirect\"后不会经过视图解析器\n //return \"redirect:hello.do\"; //hello.do为另一个请求/\n }\n}\n```\n\n\n\n> 说明:\n>\n> `return \"test\"` 通过视图解析后得到路径 /WEB-INF/jsp/test.jsp\n>\n> `return \"redirect:/index.jsp\"` 使用重定向关键字\"redirect\"后不会经过视图解析器\n\n","slug":"SpringMVC/springMVC(五)结果跳转方式","published":1,"updated":"2021-09-05T10:34:11.953Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6q9004wakve8yyz5ix5","content":"

项目地址springMVC-05-resultgo

\n

理解概念

转发:将某一信息转发到当前访问路径的页面

\n

重定向:从当前页面跳转到另一页面

\n
\n

转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。

\n

重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。

\n
\n

ModelAndView

设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。

\n

比如:

\n
1
2
3
4
5
6
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1");
return mv;
}
\n

视图解析器:

\n
1
2
3
4
5
6
7
8
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
\n

解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:"/WEB-INF/jsp/"+ "test1" + ".jsp" —> "/WEB-INF/jsp/test.jsp"

\n

ServletAPI

通过设置ServletAPI(很少用了) , 不需要视图解析器

\n
    \n
  1. 通过HttpServletResponse进行输出
  2. \n
  3. 通过HttpServletResponse实现重定向
  4. \n
  5. 通过HttpServletResponse实现转发
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Controller
public class ResultGo {
@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
rsp.getWriter().println("Hello,Spring BY servlet API");
}
@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
// 重定向
rsp.sendRedirect("/index.jsp");
}
@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
//转发
req.setAttribute("msg","/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
}
}
\n

SpringMVC

通过SpringMVC来实现转发和重定向-无视图解析器

测试前,需要将视图解析器注释掉

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Controller
public class ResultSpringMVC {
@RequestMapping("/rsm/t1")
public String test1(){
// 转发一
return "/index.jsp";
}

@RequestMapping("/rsm/t2")
public String test2(){
// 转发二
return "forward:/index.jsp";
}

@RequestMapping("/rsm/t3")
public String test3(){
// 重定向
return "redirect:/index.jsp";

}
}

\n

转发:

\n

\"image-20210904160714983\"

\n

重定向:访问 http://localhost:8083/rsm/t3 后重定向到 http://localhost:8083/index.jsp

\n

\"image-20210904160851220\"

\n

使用视图解析器

添加视图解析器

\n
1
2
3
4
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
//转发
return "test"; //通过视图解析后得到路径 /WEB-INF/jsp/test.jsp
}
@RequestMapping("/rsm2/t2")
public String test2(){
//重定向
return "redirect:/index.jsp"; // 使用重定向关键字"redirect"后不会经过视图解析器
//return "redirect:hello.do"; //hello.do为另一个请求/
}
}
\n
\n

说明:

\n

return "test" 通过视图解析后得到路径 /WEB-INF/jsp/test.jsp

\n

return "redirect:/index.jsp" 使用重定向关键字”redirect”后不会经过视图解析器

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

项目地址springMVC-05-resultgo

\n

理解概念

转发:将某一信息转发到当前访问路径的页面

\n

重定向:从当前页面跳转到另一页面

\n
\n

转发相当于:张三向你借钱,但是你兜里没钱,所以你去找李四借到钱之后借给张三。对于张三而言并不知道你的钱是和李四借的。

\n

重定向相当于:张三向你借钱,你兜里没钱,你告诉他李四有钱,然后张三去找李四借钱。

\n
\n

ModelAndView

设置ModelAndView对象 , 根据view的名称, 和视图解析器跳到指定的页面。

\n

比如:

\n
1
2
3
4
5
6
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1");
return mv;
}
\n

视图解析器:

\n
1
2
3
4
5
6
7
8
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
\n

解析过程:{视图解析器前缀} + viewName +{视图解析器后缀},即:"/WEB-INF/jsp/"+ "test1" + ".jsp" —> "/WEB-INF/jsp/test.jsp"

\n

ServletAPI

通过设置ServletAPI(很少用了) , 不需要视图解析器

\n
    \n
  1. 通过HttpServletResponse进行输出
  2. \n
  3. 通过HttpServletResponse实现重定向
  4. \n
  5. 通过HttpServletResponse实现转发
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Controller
public class ResultGo {
@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
rsp.getWriter().println("Hello,Spring BY servlet API");
}
@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
// 重定向
rsp.sendRedirect("/index.jsp");
}
@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
//转发
req.setAttribute("msg","/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
}
}
\n

SpringMVC

通过SpringMVC来实现转发和重定向-无视图解析器

测试前,需要将视图解析器注释掉

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Controller
public class ResultSpringMVC {
@RequestMapping("/rsm/t1")
public String test1(){
// 转发一
return "/index.jsp";
}

@RequestMapping("/rsm/t2")
public String test2(){
// 转发二
return "forward:/index.jsp";
}

@RequestMapping("/rsm/t3")
public String test3(){
// 重定向
return "redirect:/index.jsp";

}
}

\n

转发:

\n

\"image-20210904160714983\"

\n

重定向:访问 http://localhost:8083/rsm/t3 后重定向到 http://localhost:8083/index.jsp

\n

\"image-20210904160851220\"

\n

使用视图解析器

添加视图解析器

\n
1
2
3
4
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
//转发
return "test"; //通过视图解析后得到路径 /WEB-INF/jsp/test.jsp
}
@RequestMapping("/rsm2/t2")
public String test2(){
//重定向
return "redirect:/index.jsp"; // 使用重定向关键字"redirect"后不会经过视图解析器
//return "redirect:hello.do"; //hello.do为另一个请求/
}
}
\n
\n

说明:

\n

return "test" 通过视图解析后得到路径 /WEB-INF/jsp/test.jsp

\n

return "redirect:/index.jsp" 使用重定向关键字”redirect”后不会经过视图解析器

\n
\n"},{"title":"SpringMVC(四)Controller与RestFul","description":"Controller详解","cover":"https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png","abbrlink":"57ac54fe","date":"2021-09-04T02:31:11.000Z","_content":"\n\n\n\n\n[项目地址springMVC-04-controller](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 创建工程\n\n首先创建一个项目,运行观察效果\n\n\n\n导入依赖\n\n**配置web.xml**\n\n```xml\n\n\n\n \n \n SpringMVC\n org.springframework.web.servlet.DispatcherServlet\n\n \n \n contextConfigLocation\n \n classpath:springmvc-servlet.xml\n \n\n \n 1\n \n\n \n \n SpringMVC\n /\n \n\n\n```\n\n\n\n**配置SpringMVC配置文件**\n\n创建 `resources/springmvc-servlet.xml`\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n\n\n```\n\n\n\n\n\n创建 `ControllerTest1`\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n }\n}\n\n```\n\n\n\n注册 bean\n\n```xml\n\n\n```\n\n\n\n\n\n配置tomcat,添加lib,运行,访问 `http://localhost:8083/t1`\n\n\n\n![image-20210904104223512](https://gitee.com/ajream/images/raw/master/img/20210904104231_image-20210904104223512.png)\n\n\n\n\n\n## 解释\n\n\n\n### 控制器Controller\n\n- 控制器复杂提供访问应用程序的行为,通常通过接口定义注解定义两种方法实现\n- 控制器负责解析用户的请求并将其转换为一个模型\n- 在Spring MVC中一个控制器类可以包含多个方法\n- 在Spring MVC中,对于Controller的配置方式有很多种\n\n\n\n\n\n### 实现Controller\n\n通常通过接口定义注解定义两种方法实现\n\n#### 通过接口实现\n\nController是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;\n\n```java\n//实现该接口的类获得控制器功能\npublic interface Controller {\n //处理请求且返回一个模型与视图对象\n ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;\n}\n```\n\n\n\n例如上面的例子:\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n //返回一个模型视图对象\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\"); //WEB-INF/jsp/test1.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后去spring MVC的配置文件注册bean\n\n```xml\n\n```\n\n\n\n> 说明:\n>\n> 实现接口Controller定义控制器是较老的办法\n>\n> 缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦\n\n\n\n\n\n#### 通过注解实现\n\n\n\n- `@Controller`注解类型用于声明Spring类的实例是一个控制器\n\n- Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描\n\n ```xml\n \n \n ```\n\n\n\n例如,创建一个 `ControllerTest2`,使用注解来实现Controller\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n//@Controller注解的类会自动添加到Spring上下文中\n@Controller\npublic class ControllerTest2{\n //映射访问路径\n @RequestMapping(\"/t2\")\n public String index(Model model){\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"这是ControllerTest2\");\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n添加注解支持\n\n```xml\n\n```\n\n运行,访问:`http://localhost:8083/t2`\n\n\n\n![image-20210904110819742](https://gitee.com/ajream/images/raw/master/img/20210904110823_image-20210904110819742.png)\n\n\n\n\n\n> 可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。\n\n\n\n\n\n\n\n### 注解RequestMapping\n\n`@RequestMapping`注解用于映射url到控制器类或一个特定的处理程序方法。\n\n可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。\n\n\n\n例如\n\n只用于方法上,访问路径是 `http://localhost:8080/h1`\n\n```java\n@Controller\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n同时用于类和方法上,访问路径是 `http://localhost:8080/admin/h1`\n\n```java\n@Controller\n@RequestMapping(\"/admin\")\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n\n\n## RestFul 风格\n\n\n\nRestful就是一个资源定位资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。\n\n\n\nRestFul风格即对访问路径进行的优化\n\n比如:如果需要传参,传统访问路径可能是:`http://localhost:8080/admin?name=ajream&password=123456`\n\n而RestFul风格的访问路径是:`http://localhost:8080/admin/ajream/123456`\n\n\n\n接下来用个例子来说明:\n\n新建一个类 `RestFulController`\n\n```java\n@Controller\npublic class RestFulController {\n\n}\n```\n\n在Spring MVC中可以使用 `@PathVariable` 注解,让方法参数的值对应绑定到一个URI模板变量上。\n\n```java\n@Controller\npublic class RestFulController {\n //映射访问路径\n @RequestMapping(\"/commit/{p1}/{p2}\")\n public String index(@PathVariable int p1, @PathVariable int p2, Model model){\n int result = p1+p2;\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"Result:\"+result);\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n{%note info flat%}\n\n注意 p1, p2 均为int类型变量,如果传入 `a`, `b` ...这些字符就会出错\n\n{%endnote%}\n\n\n\n运行测试:\n\n\"image-20210904145334321\"\n\n\n\n\"image-20210904145629896\"\n\n\n\n## 使用method属性指定请求方式\n\n\n\n请求方式常用的有:get、post、...\n\n在注解 `@RequestMapping` 使用`mothod` 属性可以指定请求方式\n\n如:\n\n```java\n@Controller\npublic class RestFulController {\n\t//映射访问路径,必须是POST请求\n @RequestMapping(value = \"/t3/{a}/{b}\", method = RequestMethod.POST)\n public String test(@PathVariable int a, @PathVariable int b, Model model){\n\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n\n return \"test1\";\n\n }\n}\n```\n\n使用浏览器地址栏进行访问默认是Get请求,会报错405:\n\n![image-20210904150415014](https://gitee.com/ajream/images/raw/master/img/20210904150419_image-20210904150415014.png)\n\n\n\n可以同时使用多种请求方式:\n\n```java\n@RequestMapping(value = \"/t3/{a}/{b}\", method = {RequestMethod.POST, RequestMethod.GET})\n```\n\n\n\n\n\n也可以使用对应请求方式的注解(可以把它们叫做组合注解):\n\n```java\n@GetMapping(\"/t1\") \t\t//等价于 @RequestMapping(value=\"t1\", method=RequestMethod.GET)\n@PostMapping(\"/t1\")\n@PutMapping(\"/t1\")\n@DeleteMapping(\"/t1\")\n@PatchMapping(\"/t1\")\n```\n\n例如\n\n```java\n@GetMapping(\"/t3/{a}/{b}\")\npublic String test(@PathVariable int a, @PathVariable int b, Model model){\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n return \"test1\";\n\n}\n```\n\n","source":"_posts/SpringMVC/springMVC(四)Controller与restful.md","raw":"---\ntitle: SpringMVC(四)Controller与RestFul\ntags:\n - springMVC\ncategories:\n - - java\n - springMVC\ndescription: Controller详解\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210901193528_springMVC.png'\nabbrlink: 57ac54fe\ndate: 2021-09-04 10:31:11\n---\n\n\n\n\n\n[项目地址springMVC-04-controller](https://codechina.csdn.net/m0_46079750/springmvc)\n\n## 创建工程\n\n首先创建一个项目,运行观察效果\n\n\n\n导入依赖\n\n**配置web.xml**\n\n```xml\n\n\n\n \n \n SpringMVC\n org.springframework.web.servlet.DispatcherServlet\n\n \n \n contextConfigLocation\n \n classpath:springmvc-servlet.xml\n \n\n \n 1\n \n\n \n \n SpringMVC\n /\n \n\n\n```\n\n\n\n**配置SpringMVC配置文件**\n\n创建 `resources/springmvc-servlet.xml`\n\n```xml\n\n\n\n \n \n \n \n \n \n \n\n\n\n```\n\n\n\n\n\n创建 `ControllerTest1`\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\");\n return mv;\n }\n}\n\n```\n\n\n\n注册 bean\n\n```xml\n\n\n```\n\n\n\n\n\n配置tomcat,添加lib,运行,访问 `http://localhost:8083/t1`\n\n\n\n![image-20210904104223512](https://gitee.com/ajream/images/raw/master/img/20210904104231_image-20210904104223512.png)\n\n\n\n\n\n## 解释\n\n\n\n### 控制器Controller\n\n- 控制器复杂提供访问应用程序的行为,通常通过接口定义注解定义两种方法实现\n- 控制器负责解析用户的请求并将其转换为一个模型\n- 在Spring MVC中一个控制器类可以包含多个方法\n- 在Spring MVC中,对于Controller的配置方式有很多种\n\n\n\n\n\n### 实现Controller\n\n通常通过接口定义注解定义两种方法实现\n\n#### 通过接口实现\n\nController是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;\n\n```java\n//实现该接口的类获得控制器功能\npublic interface Controller {\n //处理请求且返回一个模型与视图对象\n ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;\n}\n```\n\n\n\n例如上面的例子:\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.web.servlet.ModelAndView;\nimport org.springframework.web.servlet.mvc.Controller;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n//实现Controller接口\npublic class ControllerTest1 implements Controller {\n @Override\n public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {\n //返回一个模型视图对象\n ModelAndView mv = new ModelAndView();\n mv.addObject(\"msg\", \"这是ControllerTest1\");\n mv.setViewName(\"test1\"); //WEB-INF/jsp/test1.jsp\n return mv;\n }\n}\n\n```\n\n\n\n然后去spring MVC的配置文件注册bean\n\n```xml\n\n```\n\n\n\n> 说明:\n>\n> 实现接口Controller定义控制器是较老的办法\n>\n> 缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦\n\n\n\n\n\n#### 通过注解实现\n\n\n\n- `@Controller`注解类型用于声明Spring类的实例是一个控制器\n\n- Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描\n\n ```xml\n \n \n ```\n\n\n\n例如,创建一个 `ControllerTest2`,使用注解来实现Controller\n\n```java\npackage com.ajream.controller;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.RequestMapping;\n\n//@Controller注解的类会自动添加到Spring上下文中\n@Controller\npublic class ControllerTest2{\n //映射访问路径\n @RequestMapping(\"/t2\")\n public String index(Model model){\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"这是ControllerTest2\");\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n添加注解支持\n\n```xml\n\n```\n\n运行,访问:`http://localhost:8083/t2`\n\n\n\n![image-20210904110819742](https://gitee.com/ajream/images/raw/master/img/20210904110823_image-20210904110819742.png)\n\n\n\n\n\n> 可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。\n\n\n\n\n\n\n\n### 注解RequestMapping\n\n`@RequestMapping`注解用于映射url到控制器类或一个特定的处理程序方法。\n\n可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。\n\n\n\n例如\n\n只用于方法上,访问路径是 `http://localhost:8080/h1`\n\n```java\n@Controller\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n同时用于类和方法上,访问路径是 `http://localhost:8080/admin/h1`\n\n```java\n@Controller\n@RequestMapping(\"/admin\")\npublic class TestController {\n @RequestMapping(\"/h1\")\n public String test(){\n return \"test\";\n }\n}\n```\n\n\n\n\n\n## RestFul 风格\n\n\n\nRestful就是一个资源定位资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。\n\n\n\nRestFul风格即对访问路径进行的优化\n\n比如:如果需要传参,传统访问路径可能是:`http://localhost:8080/admin?name=ajream&password=123456`\n\n而RestFul风格的访问路径是:`http://localhost:8080/admin/ajream/123456`\n\n\n\n接下来用个例子来说明:\n\n新建一个类 `RestFulController`\n\n```java\n@Controller\npublic class RestFulController {\n\n}\n```\n\n在Spring MVC中可以使用 `@PathVariable` 注解,让方法参数的值对应绑定到一个URI模板变量上。\n\n```java\n@Controller\npublic class RestFulController {\n //映射访问路径\n @RequestMapping(\"/commit/{p1}/{p2}\")\n public String index(@PathVariable int p1, @PathVariable int p2, Model model){\n int result = p1+p2;\n //Spring MVC会自动实例化一个Model对象用于向视图中传值\n model.addAttribute(\"msg\", \"Result:\"+result);\n //返回视图位置\n return \"test1\";\n }\n}\n```\n\n{%note info flat%}\n\n注意 p1, p2 均为int类型变量,如果传入 `a`, `b` ...这些字符就会出错\n\n{%endnote%}\n\n\n\n运行测试:\n\n\"image-20210904145334321\"\n\n\n\n\"image-20210904145629896\"\n\n\n\n## 使用method属性指定请求方式\n\n\n\n请求方式常用的有:get、post、...\n\n在注解 `@RequestMapping` 使用`mothod` 属性可以指定请求方式\n\n如:\n\n```java\n@Controller\npublic class RestFulController {\n\t//映射访问路径,必须是POST请求\n @RequestMapping(value = \"/t3/{a}/{b}\", method = RequestMethod.POST)\n public String test(@PathVariable int a, @PathVariable int b, Model model){\n\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n\n return \"test1\";\n\n }\n}\n```\n\n使用浏览器地址栏进行访问默认是Get请求,会报错405:\n\n![image-20210904150415014](https://gitee.com/ajream/images/raw/master/img/20210904150419_image-20210904150415014.png)\n\n\n\n可以同时使用多种请求方式:\n\n```java\n@RequestMapping(value = \"/t3/{a}/{b}\", method = {RequestMethod.POST, RequestMethod.GET})\n```\n\n\n\n\n\n也可以使用对应请求方式的注解(可以把它们叫做组合注解):\n\n```java\n@GetMapping(\"/t1\") \t\t//等价于 @RequestMapping(value=\"t1\", method=RequestMethod.GET)\n@PostMapping(\"/t1\")\n@PutMapping(\"/t1\")\n@DeleteMapping(\"/t1\")\n@PatchMapping(\"/t1\")\n```\n\n例如\n\n```java\n@GetMapping(\"/t3/{a}/{b}\")\npublic String test(@PathVariable int a, @PathVariable int b, Model model){\n int r = a + b;\n model.addAttribute(\"msg\",\"Result: \" + r);\n return \"test1\";\n\n}\n```\n\n","slug":"SpringMVC/springMVC(四)Controller与restful","published":1,"updated":"2021-09-05T10:34:07.415Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qb004yakve5qk6a50r","content":"

项目地址springMVC-04-controller

\n

创建工程

首先创建一个项目,运行观察效果

\n

导入依赖

\n

配置web.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--1.注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--在resources新建springmvc-servlet.xml文件-->
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
\n

配置SpringMVC配置文件

\n

创建 resources/springmvc-servlet.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>


</beans>
\n

创建 ControllerTest1

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ControllerTest1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1");
return mv;
}
}

\n

注册 bean

\n
1
2
<!--注意“/t1” 的斜杠不能少-->
<bean id="/t1" class="com.ajream.controller.ControllerTest1"/>
\n

配置tomcat,添加lib,运行,访问 http://localhost:8083/t1

\n

\"image-20210904104223512\"

\n

解释

控制器Controller

    \n
  • 控制器复杂提供访问应用程序的行为,通常通过接口定义注解定义两种方法实现
  • \n
  • 控制器负责解析用户的请求并将其转换为一个模型
  • \n
  • 在Spring MVC中一个控制器类可以包含多个方法
  • \n
  • 在Spring MVC中,对于Controller的配置方式有很多种
  • \n
\n

实现Controller

通常通过接口定义注解定义两种方法实现

\n

通过接口实现

Controller是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;

\n
1
2
3
4
5
//实现该接口的类获得控制器功能
public interface Controller {
//处理请求且返回一个模型与视图对象
ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}
\n

例如上面的例子:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.ajream.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//实现Controller接口
public class ControllerTest1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1"); //WEB-INF/jsp/test1.jsp
return mv;
}
}

\n

然后去spring MVC的配置文件注册bean

\n
1
<bean name="/t1" class="com.kuang.controller.ControllerTest1"/>
\n
\n

说明:

\n

实现接口Controller定义控制器是较老的办法

\n

缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦

\n
\n

通过注解实现

    \n
  • @Controller注解类型用于声明Spring类的实例是一个控制器

    \n
  • \n
  • Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描

    \n
    1
    2
    <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
    <context:component-scan base-package="com.ajream.controller"/>
    \n
  • \n
\n

例如,创建一个 ControllerTest2,使用注解来实现Controller

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

//@Controller注解的类会自动添加到Spring上下文中
@Controller
public class ControllerTest2{
//映射访问路径
@RequestMapping("/t2")
public String index(Model model){
//Spring MVC会自动实例化一个Model对象用于向视图中传值
model.addAttribute("msg", "这是ControllerTest2");
//返回视图位置
return "test1";
}
}
\n

添加注解支持

\n
1
<context:component-scan base-package="com.ajream.controller"/>
\n

运行,访问:http://localhost:8083/t2

\n

\"image-20210904110819742\"

\n
\n

可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。

\n
\n

注解RequestMapping

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。

\n

可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

\n

例如

\n

只用于方法上,访问路径是 http://localhost:8080/h1

\n
1
2
3
4
5
6
7
@Controller
public class TestController {
@RequestMapping("/h1")
public String test(){
return "test";
}
}
\n

同时用于类和方法上,访问路径是 http://localhost:8080/admin/h1

\n
1
2
3
4
5
6
7
8
@Controller
@RequestMapping("/admin")
public class TestController {
@RequestMapping("/h1")
public String test(){
return "test";
}
}
\n

RestFul 风格

Restful就是一个资源定位资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

\n

RestFul风格即对访问路径进行的优化

\n

比如:如果需要传参,传统访问路径可能是:http://localhost:8080/admin?name=ajream&password=123456

\n

而RestFul风格的访问路径是:http://localhost:8080/admin/ajream/123456

\n

接下来用个例子来说明:

\n

新建一个类 RestFulController

\n
1
2
3
4
@Controller
public class RestFulController {

}
\n

在Spring MVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上。

\n
1
2
3
4
5
6
7
8
9
10
11
12
@Controller
public class RestFulController {
//映射访问路径
@RequestMapping("/commit/{p1}/{p2}")
public String index(@PathVariable int p1, @PathVariable int p2, Model model){
int result = p1+p2;
//Spring MVC会自动实例化一个Model对象用于向视图中传值
model.addAttribute("msg", "Result:"+result);
//返回视图位置
return "test1";
}
}
\n

注意 p1, p2 均为int类型变量,如果传入 a, b …这些字符就会出错

\n
\n

运行测试:

\n

\"image-20210904145334321\"

\n

\"image-20210904145629896\"

\n

使用method属性指定请求方式

请求方式常用的有:get、post、…

\n

在注解 @RequestMapping 使用mothod 属性可以指定请求方式

\n

如:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class RestFulController {
\t//映射访问路径,必须是POST请求
@RequestMapping(value = "/t3/{a}/{b}", method = RequestMethod.POST)
public String test(@PathVariable int a, @PathVariable int b, Model model){

int r = a + b;
model.addAttribute("msg","Result: " + r);

return "test1";

}
}
\n

使用浏览器地址栏进行访问默认是Get请求,会报错405:

\n

\"image-20210904150415014\"

\n

可以同时使用多种请求方式:

\n
1
@RequestMapping(value = "/t3/{a}/{b}", method = {RequestMethod.POST, RequestMethod.GET})
\n

也可以使用对应请求方式的注解(可以把它们叫做组合注解):

\n
1
2
3
4
5
@GetMapping("/t1")    \t\t//等价于 @RequestMapping(value="t1", method=RequestMethod.GET)
@PostMapping("/t1")
@PutMapping("/t1")
@DeleteMapping("/t1")
@PatchMapping("/t1")
\n

例如

\n
1
2
3
4
5
6
7
@GetMapping("/t3/{a}/{b}")
public String test(@PathVariable int a, @PathVariable int b, Model model){
int r = a + b;
model.addAttribute("msg","Result: " + r);
return "test1";

}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

项目地址springMVC-04-controller

\n

创建工程

首先创建一个项目,运行观察效果

\n

导入依赖

\n

配置web.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--1.注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--在resources新建springmvc-servlet.xml文件-->
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
\n

配置SpringMVC配置文件

\n

创建 resources/springmvc-servlet.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>


</beans>
\n

创建 ControllerTest1

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ControllerTest1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1");
return mv;
}
}

\n

注册 bean

\n
1
2
<!--注意“/t1” 的斜杠不能少-->
<bean id="/t1" class="com.ajream.controller.ControllerTest1"/>
\n

配置tomcat,添加lib,运行,访问 http://localhost:8083/t1

\n

\"image-20210904104223512\"

\n

解释

控制器Controller

    \n
  • 控制器复杂提供访问应用程序的行为,通常通过接口定义注解定义两种方法实现
  • \n
  • 控制器负责解析用户的请求并将其转换为一个模型
  • \n
  • 在Spring MVC中一个控制器类可以包含多个方法
  • \n
  • 在Spring MVC中,对于Controller的配置方式有很多种
  • \n
\n

实现Controller

通常通过接口定义注解定义两种方法实现

\n

通过接口实现

Controller是一个接口,在org.springframework.web.servlet.mvc包下,接口中只有一个方法;

\n
1
2
3
4
5
//实现该接口的类获得控制器功能
public interface Controller {
//处理请求且返回一个模型与视图对象
ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
}
\n

例如上面的例子:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.ajream.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//实现Controller接口
public class ControllerTest1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg", "这是ControllerTest1");
mv.setViewName("test1"); //WEB-INF/jsp/test1.jsp
return mv;
}
}

\n

然后去spring MVC的配置文件注册bean

\n
1
<bean name="/t1" class="com.kuang.controller.ControllerTest1"/>
\n
\n

说明:

\n

实现接口Controller定义控制器是较老的办法

\n

缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦

\n
\n

通过注解实现

    \n
  • @Controller注解类型用于声明Spring类的实例是一个控制器

    \n
  • \n
  • Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描

    \n
    1
    2
    <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
    <context:component-scan base-package="com.ajream.controller"/>
    \n
  • \n
\n

例如,创建一个 ControllerTest2,使用注解来实现Controller

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ajream.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

//@Controller注解的类会自动添加到Spring上下文中
@Controller
public class ControllerTest2{
//映射访问路径
@RequestMapping("/t2")
public String index(Model model){
//Spring MVC会自动实例化一个Model对象用于向视图中传值
model.addAttribute("msg", "这是ControllerTest2");
//返回视图位置
return "test1";
}
}
\n

添加注解支持

\n
1
<context:component-scan base-package="com.ajream.controller"/>
\n

运行,访问:http://localhost:8083/t2

\n

\"image-20210904110819742\"

\n
\n

可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。

\n
\n

注解RequestMapping

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。

\n

可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

\n

例如

\n

只用于方法上,访问路径是 http://localhost:8080/h1

\n
1
2
3
4
5
6
7
@Controller
public class TestController {
@RequestMapping("/h1")
public String test(){
return "test";
}
}
\n

同时用于类和方法上,访问路径是 http://localhost:8080/admin/h1

\n
1
2
3
4
5
6
7
8
@Controller
@RequestMapping("/admin")
public class TestController {
@RequestMapping("/h1")
public String test(){
return "test";
}
}
\n

RestFul 风格

Restful就是一个资源定位资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

\n

RestFul风格即对访问路径进行的优化

\n

比如:如果需要传参,传统访问路径可能是:http://localhost:8080/admin?name=ajream&password=123456

\n

而RestFul风格的访问路径是:http://localhost:8080/admin/ajream/123456

\n

接下来用个例子来说明:

\n

新建一个类 RestFulController

\n
1
2
3
4
@Controller
public class RestFulController {

}
\n

在Spring MVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上。

\n
1
2
3
4
5
6
7
8
9
10
11
12
@Controller
public class RestFulController {
//映射访问路径
@RequestMapping("/commit/{p1}/{p2}")
public String index(@PathVariable int p1, @PathVariable int p2, Model model){
int result = p1+p2;
//Spring MVC会自动实例化一个Model对象用于向视图中传值
model.addAttribute("msg", "Result:"+result);
//返回视图位置
return "test1";
}
}
\n

注意 p1, p2 均为int类型变量,如果传入 a, b …这些字符就会出错

\n
\n

运行测试:

\n

\"image-20210904145334321\"

\n

\"image-20210904145629896\"

\n

使用method属性指定请求方式

请求方式常用的有:get、post、…

\n

在注解 @RequestMapping 使用mothod 属性可以指定请求方式

\n

如:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class RestFulController {
\t//映射访问路径,必须是POST请求
@RequestMapping(value = "/t3/{a}/{b}", method = RequestMethod.POST)
public String test(@PathVariable int a, @PathVariable int b, Model model){

int r = a + b;
model.addAttribute("msg","Result: " + r);

return "test1";

}
}
\n

使用浏览器地址栏进行访问默认是Get请求,会报错405:

\n

\"image-20210904150415014\"

\n

可以同时使用多种请求方式:

\n
1
@RequestMapping(value = "/t3/{a}/{b}", method = {RequestMethod.POST, RequestMethod.GET})
\n

也可以使用对应请求方式的注解(可以把它们叫做组合注解):

\n
1
2
3
4
5
@GetMapping("/t1")    \t\t//等价于 @RequestMapping(value="t1", method=RequestMethod.GET)
@PostMapping("/t1")
@PutMapping("/t1")
@DeleteMapping("/t1")
@PatchMapping("/t1")
\n

例如

\n
1
2
3
4
5
6
7
@GetMapping("/t3/{a}/{b}")
public String test(@PathVariable int a, @PathVariable int b, Model model){
int r = a + b;
model.addAttribute("msg","Result: " + r);
return "test1";

}
\n"},{"title":"1-Servlet","abbrlink":"345d8e17","date":"2021-04-12T02:44:20.000Z","description":"Servlet举例介绍","_content":"\n\n# Servlet\n\n## servlet技术\n\n1. Servlet是Java EE规范之一,规范即接口\n2. 是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器\n3. 是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端\n4. Servlet没有 `main()`方法,由系统自动调用\n\n\n\n## IDEA创建servlet\n\n\"在这里插入图片描述\"\n\n\n\n\"image-20210502163733514\"\n\n\n\n\"image-20210502163838549\"\n\n\n\n\n\n## 手动实现Servlet程序\n\n1. 编写类实现Servlet规范,有3种方式:\n\n 1. 继承 `HttpServlet`类\n\n ```java\n public class HelloServlet extends HttpServlet {\n\n }\n ```\n\n 2. 实现 `Servlet`接口\n\n ```java\n package com.example.servlet01;\n \n import javax.servlet.*;\n import java.io.IOException;\n \n public class Servlet01 implements Servlet {\n @Override\n public void init(ServletConfig servletConfig) throws ServletException {\n \n }\n \n @Override\n public ServletConfig getServletConfig() {\n return null;\n }\n \n @Override\n public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {\n \t\tSystem.out.println(\"实现Servlet接口\");\n }\n \n @Override\n public String getServletInfo() {\n return null;\n }\n \n @Override\n public void destroy() {\n \n }\n }\n \n ```\n\n \n\n2. 实现 `service()`方法,处理请求,响应数据\n\n 【快捷键 ctrl + o 可以重写方法或实现接口】\n\n ```java\n public class HelloServlet extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n //控制台打印\n System.out.println(\"Hello Servlet\");\n \n //通过流输出数据到浏览器\n resp.getWriter().write(\"hello servlet!\");\n }\n }\n ```\n\n3. 设置注解`@WebServlet`,指定访问路径\n\n ```java\n @WebServlet(name=\"helloServlet\", value = \"/ser01\") //name可以不设置\n ```\n\n\n\n\n> 在第二步中,也可以不重写 `Service()`方法,而是更具体些,重写 `doGet()`、`doPost`方法;\n>\n> 根据浏览器请求方式来进行不同处理\n>\n> 但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少\n\n\n\n## Servlet工作流程\n\nurl`http://localhost:8083/servlet01/ser01`含义:\n\n> - 服务器:`http://localhost:8083`\n> - 项目:`/servlet01`\n> - 资源:`/ser01`\n\n","source":"_posts/javaEE/1-Servlet.md","raw":"---\ntitle: 1-Servlet\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: 345d8e17\ndate: 2021-04-12 10:44:20\ndescription: Servlet举例介绍\n---\n\n\n# Servlet\n\n## servlet技术\n\n1. Servlet是Java EE规范之一,规范即接口\n2. 是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器\n3. 是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端\n4. Servlet没有 `main()`方法,由系统自动调用\n\n\n\n## IDEA创建servlet\n\n\"在这里插入图片描述\"\n\n\n\n\"image-20210502163733514\"\n\n\n\n\"image-20210502163838549\"\n\n\n\n\n\n## 手动实现Servlet程序\n\n1. 编写类实现Servlet规范,有3种方式:\n\n 1. 继承 `HttpServlet`类\n\n ```java\n public class HelloServlet extends HttpServlet {\n\n }\n ```\n\n 2. 实现 `Servlet`接口\n\n ```java\n package com.example.servlet01;\n \n import javax.servlet.*;\n import java.io.IOException;\n \n public class Servlet01 implements Servlet {\n @Override\n public void init(ServletConfig servletConfig) throws ServletException {\n \n }\n \n @Override\n public ServletConfig getServletConfig() {\n return null;\n }\n \n @Override\n public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {\n \t\tSystem.out.println(\"实现Servlet接口\");\n }\n \n @Override\n public String getServletInfo() {\n return null;\n }\n \n @Override\n public void destroy() {\n \n }\n }\n \n ```\n\n \n\n2. 实现 `service()`方法,处理请求,响应数据\n\n 【快捷键 ctrl + o 可以重写方法或实现接口】\n\n ```java\n public class HelloServlet extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n //控制台打印\n System.out.println(\"Hello Servlet\");\n \n //通过流输出数据到浏览器\n resp.getWriter().write(\"hello servlet!\");\n }\n }\n ```\n\n3. 设置注解`@WebServlet`,指定访问路径\n\n ```java\n @WebServlet(name=\"helloServlet\", value = \"/ser01\") //name可以不设置\n ```\n\n\n\n\n> 在第二步中,也可以不重写 `Service()`方法,而是更具体些,重写 `doGet()`、`doPost`方法;\n>\n> 根据浏览器请求方式来进行不同处理\n>\n> 但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少\n\n\n\n## Servlet工作流程\n\nurl`http://localhost:8083/servlet01/ser01`含义:\n\n> - 服务器:`http://localhost:8083`\n> - 项目:`/servlet01`\n> - 资源:`/ser01`\n\n","slug":"javaEE/1-Servlet","published":1,"updated":"2021-09-10T03:08:58.332Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qc0051akve1nql6qt7","content":"

Servlet

servlet技术

    \n
  1. Servlet是Java EE规范之一,规范即接口
  2. \n
  3. 是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器
  4. \n
  5. 是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端
  6. \n
  7. Servlet没有 main()方法,由系统自动调用
  8. \n
\n

IDEA创建servlet

\"在这里插入图片描述\"

\n

\"image-20210502163733514\"

\n

\"image-20210502163838549\"

\n

手动实现Servlet程序

    \n
  1. 编写类实现Servlet规范,有3种方式:

    \n
      \n
    1. 继承 HttpServlet

      \n
      1
      2
      3
      public class HelloServlet extends HttpServlet {

      }
      \n
    2. \n
    3. 实现 Servlet接口

      \n
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      package com.example.servlet01;

      import javax.servlet.*;
      import java.io.IOException;

      public class Servlet01 implements Servlet {
      @Override
      public void init(ServletConfig servletConfig) throws ServletException {

      }

      @Override
      public ServletConfig getServletConfig() {
      return null;
      }

      @Override
      public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
      \t\tSystem.out.println("实现Servlet接口");
      }

      @Override
      public String getServletInfo() {
      return null;
      }

      @Override
      public void destroy() {

      }
      }

      \n
    4. \n
    \n
  2. \n
\n
    \n
  1. 实现 service()方法,处理请求,响应数据

    \n

    【快捷键 ctrl + o 可以重写方法或实现接口】

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //控制台打印
    System.out.println("Hello Servlet");

    //通过流输出数据到浏览器
    resp.getWriter().write("hello servlet!");
    }
    }
    \n
  2. \n
  3. 设置注解@WebServlet,指定访问路径

    \n
    1
    @WebServlet(name="helloServlet", value = "/ser01") //name可以不设置
    \n
  4. \n
\n
\n

在第二步中,也可以不重写 Service()方法,而是更具体些,重写 doGet()doPost方法;

\n

根据浏览器请求方式来进行不同处理

\n

但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少

\n
\n

Servlet工作流程

urlhttp://localhost:8083/servlet01/ser01含义:

\n
\n
    \n
  • 服务器:http://localhost:8083
  • \n
  • 项目:/servlet01
  • \n
  • 资源:/ser01
  • \n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

Servlet

servlet技术

    \n
  1. Servlet是Java EE规范之一,规范即接口
  2. \n
  3. 是JavaWeb三大组件之一,其他2个是:Filter过滤器、Listener监听器
  4. \n
  5. 是运行在服务器上一个Java小程序,可以接收客户端发来的请求,并相应给客户端
  6. \n
  7. Servlet没有 main()方法,由系统自动调用
  8. \n
\n

IDEA创建servlet

\"在这里插入图片描述\"

\n

\"image-20210502163733514\"

\n

\"image-20210502163838549\"

\n

手动实现Servlet程序

    \n
  1. 编写类实现Servlet规范,有3种方式:

    \n
      \n
    1. 继承 HttpServlet

      \n
      1
      2
      3
      public class HelloServlet extends HttpServlet {

      }
      \n
    2. \n
    3. 实现 Servlet接口

      \n
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      package com.example.servlet01;

      import javax.servlet.*;
      import java.io.IOException;

      public class Servlet01 implements Servlet {
      @Override
      public void init(ServletConfig servletConfig) throws ServletException {

      }

      @Override
      public ServletConfig getServletConfig() {
      return null;
      }

      @Override
      public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
      \t\tSystem.out.println("实现Servlet接口");
      }

      @Override
      public String getServletInfo() {
      return null;
      }

      @Override
      public void destroy() {

      }
      }

      \n
    4. \n
    \n
  2. \n
\n
    \n
  1. 实现 service()方法,处理请求,响应数据

    \n

    【快捷键 ctrl + o 可以重写方法或实现接口】

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //控制台打印
    System.out.println("Hello Servlet");

    //通过流输出数据到浏览器
    resp.getWriter().write("hello servlet!");
    }
    }
    \n
  2. \n
  3. 设置注解@WebServlet,指定访问路径

    \n
    1
    @WebServlet(name="helloServlet", value = "/ser01") //name可以不设置
    \n
  4. \n
\n
\n

在第二步中,也可以不重写 Service()方法,而是更具体些,重写 doGet()doPost方法;

\n

根据浏览器请求方式来进行不同处理

\n

但实际我们不知道浏览器以何种方式请求,故两个方法都需要去写,因此这种方式用的较少

\n
\n

Servlet工作流程

urlhttp://localhost:8083/servlet01/ser01含义:

\n
\n
    \n
  • 服务器:http://localhost:8083
  • \n
  • 项目:/servlet01
  • \n
  • 资源:/ser01
  • \n
\n
\n"},{"title":"2-Servlet生命周期","abbrlink":"e7ba7594","date":"2021-04-12T08:27:24.000Z","description":"Servlet的几个生命周期","_content":"\n\n# Servlet生命周期\n\n\n\n1. 通过调用 `init()`方法创建、初始化服务(系统自动调用,只执行一次)\n2. 调用 `Service()` 方法执行服务\n3. 调用 `destroy()`销毁服务(关闭服务器时系统自动调用,只执行一次)\n\n\n\n#### Servlet工作流程\n\n![image-20210502165959933](https://gitee.com/ajream/images/raw/master/img/2021-05-0217-00-01_image-20210502165959933.png)","source":"_posts/javaEE/2-Servlet生命周期.md","raw":"---\ntitle: 2-Servlet生命周期\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: e7ba7594\ndate: 2021-04-12 16:27:24\ndescription: Servlet的几个生命周期\n---\n\n\n# Servlet生命周期\n\n\n\n1. 通过调用 `init()`方法创建、初始化服务(系统自动调用,只执行一次)\n2. 调用 `Service()` 方法执行服务\n3. 调用 `destroy()`销毁服务(关闭服务器时系统自动调用,只执行一次)\n\n\n\n#### Servlet工作流程\n\n![image-20210502165959933](https://gitee.com/ajream/images/raw/master/img/2021-05-0217-00-01_image-20210502165959933.png)","slug":"javaEE/2-Servlet生命周期","published":1,"updated":"2021-09-10T03:09:15.138Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qd0053akve87tvdadq","content":"

Servlet生命周期

    \n
  1. 通过调用 init()方法创建、初始化服务(系统自动调用,只执行一次)
  2. \n
  3. 调用 Service() 方法执行服务
  4. \n
  5. 调用 destroy()销毁服务(关闭服务器时系统自动调用,只执行一次)
  6. \n
\n

Servlet工作流程

\"image-20210502165959933\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"

Servlet生命周期

    \n
  1. 通过调用 init()方法创建、初始化服务(系统自动调用,只执行一次)
  2. \n
  3. 调用 Service() 方法执行服务
  4. \n
  5. 调用 destroy()销毁服务(关闭服务器时系统自动调用,只执行一次)
  6. \n
\n

Servlet工作流程

\"image-20210502165959933\"

\n"},{"title":"4-请求转发","abbrlink":"1a9b2152","date":"2021-04-14T01:34:29.000Z","description":"Servlet如何处理客户端发来的请求","_content":"\n# 请求转发\n\n1. 可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理)\n2. 是服务端行为\n3. 特点:\n 1. 浏览器地址栏不发生改变\n 2. 实现不同的Servlet数据贡献\n\n\n\n步骤:\n\n1. 分别创建类:Servlet02和Servlet03\n\n 填入以下内容:\n\n ```java\n //类Servlet02\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser02\")\n public class Servlet02 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser02获取到用户名:\" + name);\n \n //跳转到servlet03\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n }\n }\n \n ```\n\n \n\n ```java\n //类Servlet03\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser03\")\n public class servlet03 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser03获取到用户名:\" + name);\n \n }\n }\n \n ```\n\n 在类Servlet02中,跳转语句为:\n\n ```java\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n ```\n\n `ser03`(没有斜线“/”)表示servlet03的注解 “**@WebServlet(\"/ser03\")**”\n\n \n\n > 也可以跳转到 login.jsp页面\n >\n > ```java\n > //跳转到login.jsp\n > req.getRequestDispatcher(\"login.jsp\").forward(req, resp);\n > ```\n\n \n\n2. 启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变\n\n ![image-20210502211542653](https://gitee.com/aJream/images/raw/master/img/2021-05-0221-15-48_image-20210502211542653.png)\n\n \n\n ![image-20210502211627393](https://gitee.com/ajream/images/raw/master/img/2021-05-0221-16-32_image-20210502211627393.png)\n\n\n\n","source":"_posts/javaEE/4-请求转发.md","raw":"---\ntitle: 4-请求转发\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: 1a9b2152\ndate: 2021-04-14 09:34:29\ndescription: Servlet如何处理客户端发来的请求\n---\n\n# 请求转发\n\n1. 可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理)\n2. 是服务端行为\n3. 特点:\n 1. 浏览器地址栏不发生改变\n 2. 实现不同的Servlet数据贡献\n\n\n\n步骤:\n\n1. 分别创建类:Servlet02和Servlet03\n\n 填入以下内容:\n\n ```java\n //类Servlet02\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser02\")\n public class Servlet02 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser02获取到用户名:\" + name);\n \n //跳转到servlet03\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n }\n }\n \n ```\n\n \n\n ```java\n //类Servlet03\n \n package com.xxxx.servlet02;\n \n import javax.servlet.ServletException;\n import javax.servlet.annotation.WebServlet;\n import javax.servlet.http.HttpServlet;\n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n import java.io.IOException;\n \n @WebServlet(\"/ser03\")\n public class servlet03 extends HttpServlet {\n @Override\n protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n String name = req.getParameter(\"name\");\n System.out.println(\"ser03获取到用户名:\" + name);\n \n }\n }\n \n ```\n\n 在类Servlet02中,跳转语句为:\n\n ```java\n req.getRequestDispatcher(\"ser03\").forward(req, resp);\n ```\n\n `ser03`(没有斜线“/”)表示servlet03的注解 “**@WebServlet(\"/ser03\")**”\n\n \n\n > 也可以跳转到 login.jsp页面\n >\n > ```java\n > //跳转到login.jsp\n > req.getRequestDispatcher(\"login.jsp\").forward(req, resp);\n > ```\n\n \n\n2. 启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变\n\n ![image-20210502211542653](https://gitee.com/aJream/images/raw/master/img/2021-05-0221-15-48_image-20210502211542653.png)\n\n \n\n ![image-20210502211627393](https://gitee.com/ajream/images/raw/master/img/2021-05-0221-16-32_image-20210502211627393.png)\n\n\n\n","slug":"javaEE/4-请求转发","published":1,"updated":"2021-09-10T03:10:21.610Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qe0056akvea9ks9ha1","content":"

请求转发

    \n
  1. 可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理)
  2. \n
  3. 是服务端行为
  4. \n
  5. 特点:
      \n
    1. 浏览器地址栏不发生改变
    2. \n
    3. 实现不同的Servlet数据贡献
    4. \n
    \n
  6. \n
\n

步骤:

\n
    \n
  1. 分别创建类:Servlet02和Servlet03

    \n

    填入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //类Servlet02

    package com.xxxx.servlet02;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    @WebServlet("/ser02")
    public class Servlet02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String name = req.getParameter("name");
    System.out.println("ser02获取到用户名:" + name);

    //跳转到servlet03
    req.getRequestDispatcher("ser03").forward(req, resp);
    }
    }

    \n
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//类Servlet03

package com.xxxx.servlet02;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/ser03")
public class servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
System.out.println("ser03获取到用户名:" + name);

}
}

\n

在类Servlet02中,跳转语句为:

\n
1
req.getRequestDispatcher("ser03").forward(req, resp);
\n

ser03(没有斜线“/”)表示servlet03的注解 “@WebServlet(“/ser03”)

\n
\n

也可以跳转到 login.jsp页面

\n
1
2
//跳转到login.jsp
req.getRequestDispatcher("login.jsp").forward(req, resp);
\n
\n
    \n
  1. 启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变

    \n

    \"image-20210502211542653\"

    \n
  2. \n
\n

\"image-20210502211627393\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

请求转发

    \n
  1. 可以让请求从服务端跳转到另一页面(或者跳转到另一个Servlet类处理)
  2. \n
  3. 是服务端行为
  4. \n
  5. 特点:
      \n
    1. 浏览器地址栏不发生改变
    2. \n
    3. 实现不同的Servlet数据贡献
    4. \n
    \n
  6. \n
\n

步骤:

\n
    \n
  1. 分别创建类:Servlet02和Servlet03

    \n

    填入以下内容:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //类Servlet02

    package com.xxxx.servlet02;

    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    @WebServlet("/ser02")
    public class Servlet02 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String name = req.getParameter("name");
    System.out.println("ser02获取到用户名:" + name);

    //跳转到servlet03
    req.getRequestDispatcher("ser03").forward(req, resp);
    }
    }

    \n
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//类Servlet03

package com.xxxx.servlet02;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/ser03")
public class servlet03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
System.out.println("ser03获取到用户名:" + name);

}
}

\n

在类Servlet02中,跳转语句为:

\n
1
req.getRequestDispatcher("ser03").forward(req, resp);
\n

ser03(没有斜线“/”)表示servlet03的注解 “@WebServlet(“/ser03”)

\n
\n

也可以跳转到 login.jsp页面

\n
1
2
//跳转到login.jsp
req.getRequestDispatcher("login.jsp").forward(req, resp);
\n
\n
    \n
  1. 启动服务,浏览器访问servlet02,然后自动会访问servlet03,但地址栏没变

    \n

    \"image-20210502211542653\"

    \n
  2. \n
\n

\"image-20210502211627393\"

\n"},{"title":"IDEA配置Tomcat","abbrlink":"1a57c6b6","date":"2021-06-10T06:51:17.000Z","description":"关于如何在idea中配置Tomcat服务器的具体操作","_content":"\n\n\n三个步骤\n\n1. \n\n![image-20210502152308870](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-23-11_image-20210502152308870.png)\n\n2. \n\n![image-20210502152803463](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-28-04_image-20210502152803463.png)\n\n3. \n\n![image-20210502152955436](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-29-56_image-20210502152955436.png)\n\n\n\n","source":"_posts/javaEE/IDEA配置Tomcat.md","raw":"---\ntitle: IDEA配置Tomcat\ntags:\n - Tomcat\ncategories:\n - - java\n - javaEE\nabbrlink: 1a57c6b6\ndate: 2021-06-10 14:51:17\ndescription: 关于如何在idea中配置Tomcat服务器的具体操作\n---\n\n\n\n三个步骤\n\n1. \n\n![image-20210502152308870](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-23-11_image-20210502152308870.png)\n\n2. \n\n![image-20210502152803463](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-28-04_image-20210502152803463.png)\n\n3. \n\n![image-20210502152955436](https://gitee.com/ajream/images/raw/master/img/2021-05-0215-29-56_image-20210502152955436.png)\n\n\n\n","slug":"javaEE/IDEA配置Tomcat","published":1,"updated":"2021-09-10T03:11:01.852Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qf0058akve50qfda62","content":"

三个步骤

\n
    \n
  1. \"image-20210502152308870\"

    \n
  2. \n
  3. \n
\n

\"image-20210502152803463\"

\n
    \n
  1. \"image-20210502152955436\"

    \n
  2. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

三个步骤

\n
    \n
  1. \"image-20210502152308870\"

    \n
  2. \n
  3. \n
\n

\"image-20210502152803463\"

\n
    \n
  1. \"image-20210502152955436\"

    \n
  2. \n
\n"},{"title":"3-ServletRequest对象","abbrlink":"4609d38b","date":"2021-04-13T10:27:34.000Z","description":"Servlet的Request对象使用","_content":"\n\n\n\n\n\n# HttpServletRequest对象\n\n\n\n该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等\n\n\n\nHttpServletRequest是ServletRequest的唯一的子**接口**\n\n\n\n## 接收请求\n\n### 常用方法\n\n```java\n//完整url路径\nString url = req.getRequestURL().toString();\n\n//部分路径\nString uri = req.getRequestURI();\n\n//获取请求的参数(如果有的话,即路径中`?`后面的字符串)\nString query = req.getQueryString();\n\n//请求方式get、post\nString method = req.getMethod();\n\n//请求协议(http/1/1)\nString protocol = req.getProtocol();\n\n//获取项目站点名\nString webapp = req.getContextPath();\n\n```\n\n请求为【`localhost:8083/sr02/servlet02`】时输出:\n\n![image-20210502192535571](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-25-40_image-20210502192535571.png)\n\n请求为【`localhost:8083/sr02/servlet02?name=xiaod&passwd=123456`】时:\n\n![image-20210502192917681](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-29-20_image-20210502192917681.png)\n\n\n\n**详细参数获取(重点)**\n\n```java\n//获取指定参数,返回字符串\nString name = req.getParameter(\"name\"); //用户名\nString passwd = req.getParameter(\"passwd\"); //密码\n```\n\n![image-20210502193840780](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-38-47_image-20210502193840780.png)\n\n\n\n获取多个同名参数(适用于复选框)\n\n```java\n//多个参数名称相同,用数组收集\n//如:参数为 name=xiaod&passwd=123456&hobby=sing&hobby=dance\nString[] hobbies = req.getParameterValues(\"hobby\"); \n```\n\n\n\n\n\n## 中文乱码问题\n\ntomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决\n\n\n\n使用**post请求**提交表单时,若含有中文,获取到的是乱码\n\n**验证步骤**\n\n1. 在webapp下创建login.jsp文件\n\n \"image-20210502201756825\"\n\n2. 在login.jsp下粘贴以下代码\n\n ```jsp\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n 登录\n
\n 账户名:
\n 密码:
\n \n
\n \n \n \n \n \n \n ```\n\n > 需要注意,form标签的属性 `action`为**注解@WebServlet(\"/servlet02\")**中的 `servlet02`(不要有斜线),这样就与这个处理类关联起来了\n\n3. 运行启动\n\n4. 浏览器访问\n\n ![image-20210502202959582](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-30-03_image-20210502202959582.png)\n\n5. 点击登录提交给 `Servlet01`类(含有注解 `@WebServlet(\"/servlet02\")` )处理\n\n 查看获取到的数据:\n\n ![image-20210502203233059](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-32-36_image-20210502203233059.png)\n\n","source":"_posts/javaEE/3-ServletRequest对象.md","raw":"---\ntitle: 3-ServletRequest对象\ntags:\n - Servlet\ncategories:\n - - java\n - javaEE\nabbrlink: 4609d38b\ndate: 2021-04-13 18:27:34\ndescription: Servlet的Request对象使用\n---\n\n\n\n\n\n\n# HttpServletRequest对象\n\n\n\n该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等\n\n\n\nHttpServletRequest是ServletRequest的唯一的子**接口**\n\n\n\n## 接收请求\n\n### 常用方法\n\n```java\n//完整url路径\nString url = req.getRequestURL().toString();\n\n//部分路径\nString uri = req.getRequestURI();\n\n//获取请求的参数(如果有的话,即路径中`?`后面的字符串)\nString query = req.getQueryString();\n\n//请求方式get、post\nString method = req.getMethod();\n\n//请求协议(http/1/1)\nString protocol = req.getProtocol();\n\n//获取项目站点名\nString webapp = req.getContextPath();\n\n```\n\n请求为【`localhost:8083/sr02/servlet02`】时输出:\n\n![image-20210502192535571](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-25-40_image-20210502192535571.png)\n\n请求为【`localhost:8083/sr02/servlet02?name=xiaod&passwd=123456`】时:\n\n![image-20210502192917681](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-29-20_image-20210502192917681.png)\n\n\n\n**详细参数获取(重点)**\n\n```java\n//获取指定参数,返回字符串\nString name = req.getParameter(\"name\"); //用户名\nString passwd = req.getParameter(\"passwd\"); //密码\n```\n\n![image-20210502193840780](https://gitee.com/ajream/images/raw/master/img/2021-05-0219-38-47_image-20210502193840780.png)\n\n\n\n获取多个同名参数(适用于复选框)\n\n```java\n//多个参数名称相同,用数组收集\n//如:参数为 name=xiaod&passwd=123456&hobby=sing&hobby=dance\nString[] hobbies = req.getParameterValues(\"hobby\"); \n```\n\n\n\n\n\n## 中文乱码问题\n\ntomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决\n\n\n\n使用**post请求**提交表单时,若含有中文,获取到的是乱码\n\n**验证步骤**\n\n1. 在webapp下创建login.jsp文件\n\n \"image-20210502201756825\"\n\n2. 在login.jsp下粘贴以下代码\n\n ```jsp\n <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>\n \n \n 登录\n
\n 账户名:
\n 密码:
\n \n
\n \n \n \n \n \n \n ```\n\n > 需要注意,form标签的属性 `action`为**注解@WebServlet(\"/servlet02\")**中的 `servlet02`(不要有斜线),这样就与这个处理类关联起来了\n\n3. 运行启动\n\n4. 浏览器访问\n\n ![image-20210502202959582](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-30-03_image-20210502202959582.png)\n\n5. 点击登录提交给 `Servlet01`类(含有注解 `@WebServlet(\"/servlet02\")` )处理\n\n 查看获取到的数据:\n\n ![image-20210502203233059](https://gitee.com/ajream/images/raw/master/img/2021-05-0220-32-36_image-20210502203233059.png)\n\n","slug":"javaEE/3-ServletRequest对象","published":1,"updated":"2021-09-10T03:10:07.722Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qg005aakveh3vd1y50","content":"

HttpServletRequest对象

该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等

\n

HttpServletRequest是ServletRequest的唯一的子接口

\n

接收请求

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//完整url路径
String url = req.getRequestURL().toString();

//部分路径
String uri = req.getRequestURI();

//获取请求的参数(如果有的话,即路径中`?`后面的字符串)
String query = req.getQueryString();

//请求方式get、post
String method = req.getMethod();

//请求协议(http/1/1)
String protocol = req.getProtocol();

//获取项目站点名
String webapp = req.getContextPath();

\n

请求为【localhost:8083/sr02/servlet02】时输出:

\n

\"image-20210502192535571\"

\n

请求为【localhost:8083/sr02/servlet02?name=xiaod&passwd=123456】时:

\n

\"image-20210502192917681\"

\n

详细参数获取(重点)

\n
1
2
3
//获取指定参数,返回字符串
String name = req.getParameter("name"); //用户名
String passwd = req.getParameter("passwd"); //密码
\n

\"image-20210502193840780\"

\n

获取多个同名参数(适用于复选框)

\n
1
2
3
//多个参数名称相同,用数组收集
//如:参数为 name=xiaod&passwd=123456&hobby=sing&hobby=dance
String[] hobbies = req.getParameterValues("hobby");
\n

中文乱码问题

tomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决

\n

使用post请求提交表单时,若含有中文,获取到的是乱码

\n

验证步骤

\n
    \n
  1. 在webapp下创建login.jsp文件

    \n

    \"image-20210502201756825\"

    \n
  2. \n
  3. 在login.jsp下粘贴以下代码

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>登录</title>
    <form method="post" action="servlet02">
    账户名:<input type="text" name="name"> <br>
    密码:<input type="password" name="passwd"> <br>
    <button>登录</button> <!--button默认为提交按钮-->
    </form>
    </head>
    <body>

    </body>
    </html>

    \n
    \n

    需要注意,form标签的属性 action注解@WebServlet(“/servlet02”)中的 servlet02(不要有斜线),这样就与这个处理类关联起来了

    \n
    \n
  4. \n
  5. 运行启动

    \n
  6. \n
  7. 浏览器访问

    \n

    \"image-20210502202959582\"

    \n
  8. \n
  9. 点击登录提交给 Servlet01类(含有注解 @WebServlet("/servlet02") )处理

    \n

    查看获取到的数据:

    \n

    \"image-20210502203233059\"

    \n
  10. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

HttpServletRequest对象

该对象主要用于接收客户端发送过来的信息,如请求参数、请求头等

\n

HttpServletRequest是ServletRequest的唯一的子接口

\n

接收请求

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//完整url路径
String url = req.getRequestURL().toString();

//部分路径
String uri = req.getRequestURI();

//获取请求的参数(如果有的话,即路径中`?`后面的字符串)
String query = req.getQueryString();

//请求方式get、post
String method = req.getMethod();

//请求协议(http/1/1)
String protocol = req.getProtocol();

//获取项目站点名
String webapp = req.getContextPath();

\n

请求为【localhost:8083/sr02/servlet02】时输出:

\n

\"image-20210502192535571\"

\n

请求为【localhost:8083/sr02/servlet02?name=xiaod&passwd=123456】时:

\n

\"image-20210502192917681\"

\n

详细参数获取(重点)

\n
1
2
3
//获取指定参数,返回字符串
String name = req.getParameter("name"); //用户名
String passwd = req.getParameter("passwd"); //密码
\n

\"image-20210502193840780\"

\n

获取多个同名参数(适用于复选框)

\n
1
2
3
//多个参数名称相同,用数组收集
//如:参数为 name=xiaod&passwd=123456&hobby=sing&hobby=dance
String[] hobbies = req.getParameterValues("hobby");
\n

中文乱码问题

tomcat8以上版本只有get请求会乱码,使用字符串乱码处理方式来解决

\n

使用post请求提交表单时,若含有中文,获取到的是乱码

\n

验证步骤

\n
    \n
  1. 在webapp下创建login.jsp文件

    \n

    \"image-20210502201756825\"

    \n
  2. \n
  3. 在login.jsp下粘贴以下代码

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
    <title>登录</title>
    <form method="post" action="servlet02">
    账户名:<input type="text" name="name"> <br>
    密码:<input type="password" name="passwd"> <br>
    <button>登录</button> <!--button默认为提交按钮-->
    </form>
    </head>
    <body>

    </body>
    </html>

    \n
    \n

    需要注意,form标签的属性 action注解@WebServlet(“/servlet02”)中的 servlet02(不要有斜线),这样就与这个处理类关联起来了

    \n
    \n
  4. \n
  5. 运行启动

    \n
  6. \n
  7. 浏览器访问

    \n

    \"image-20210502202959582\"

    \n
  8. \n
  9. 点击登录提交给 Servlet01类(含有注解 @WebServlet("/servlet02") )处理

    \n

    查看获取到的数据:

    \n

    \"image-20210502203233059\"

    \n
  10. \n
\n"},{"title":"1-类与对象","abbrlink":"52b44ec7","date":"2021-02-05T05:12:19.000Z","description":"关于Java的类的基本知识,例如类的属性、方法(传参及调用)、继承、this关键字等","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n\n# 类与对象\n\n类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;\n\n用代码创建一个鸟类:\n\n```java\npublic class Bird{\n String name;\n String feathers_color;\n public void fly(){\n System.out.println(name + \"可以飞\")\n }\n}\n```\n\n对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;\n\n用代码来创建对象:\n\n```java\nBird bird1 = new Bird();\nBird bird2 = new Bird();\n```\n\n## 引用与指向\n\n在下面创建的这一对象中\n\n```java\nBird bird1 = new Bird();\n```\n\n`bird1`存储的是对象的地址,因此`bird1`是对象的**引用**;这一地址**指向**了对象本身;\n\n\"image-20210123153305198\"\n\n- 多个引用指向同一对象\n\n```java\nBird b1 = new Bird();\nBird b2 = b1;\nBird b3 = b1;\n```\n\n\"image-20210123154400728\"\n\n- 多个引用指向多个(不同)对象\n\n```java\nBird b1 = new Bird();\nBird b2 = new Bird();\nBird b3 = new Bird();\n```\n\n\"image-20210123154635298\"\n\n## 继承\n\n可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:\n\n```java\npublic class ClassName2 extends ClassName1{\n\n}\n```\n\n例如:\n\n首先创建父类Item\n\n```java\npublic class Item {\n String name;\n int price;\n}\n```\n\n创建子类Weapon继承Item\n\n```java\npublic class Weapon extends Item{\n int damage; //攻击力\n \n public static void main(String[] args) {\n Weapon infinityEdge = new Weapon();\n infinityEdge.damage = 65; //damage属性在类Weapon中新设计的\n \n infinityEdge.name = \"无尽之刃\";//name属性,是从Item中继承来的,就不需要重复设计了\n infinityEdge.price = 3600;\n \n }\n \n}\n```\n\n## 方法重载\n\n方法的重载指的是**方法名一样**,但是参数类型或数量不一样\n\n```java\npublic class ADHero extends Hero {\n //无参数\n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n \t//1个参数\n public void attack(Hero h1) {\n System.out.println(name + \"对\" + h1.name + \"进行了一次攻击 \");\n }\n \t\n //2个参数\n public void attack(Hero h1, Hero h2) {\n System.out.println(name + \"同时对\" + h1.name + \"和\" + h2.name + \"进行了攻击 \");\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n }\n \n}\n```\n\n可变数量的参数\n\n采用可变数量的参数 **只需要设计一个方法**:\n`public void attack(Hero... heros)`\n即可代表上述所有的方法了,此时 `heros`被当作一个**数组**,在方法里,使用操作数组的方式处理参数heros即可\n\n```java\npublic class ADHero extends Hero {\n \n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n // 可变数量的参数\n public void attack(Hero... heros) {\n for (int i = 0; i < heros.length; i++) {\n System.out.println(name + \" 攻击了 \" + heros[i].name);\n \n }\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n \n }\n \n}\n```\n\n## 构造方法\n\n构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法\n\n```java\npublic class Hero {\n \n String name;\n \n float hp;\n \n float armor;\n \n int moveSpeed;\n \n // 方法名和类名一样(包括大小写)\n // 没有返回类型\n public Hero() {\n System.out.println(\"实例化一个对象的时候,必然调用构造方法\");\n }\n \n public static void main(String[] args) {\n //实例化一个对象的时候,必然调用构造方法\n Hero h = new Hero();\n }\n \n}\n\n//输出:实例化一个对象的时候,必然调用构造方法\n```\n\n构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同\n\n## this\n\n`this` 代表**当前对象**,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有\n\n```java\npublic class Hero {\n \n String name; //姓名\n \n float hp; //血量\n \n float armor; //护甲\n \n int moveSpeed; //移动速度\n \n //打印内存中的虚拟地址\n public void showAddressInMemory(){\n System.out.println(\"打印this看到的虚拟地址:\"+this);\n }\n \n public static void main(String[] args) {\n //---------------------------------------------\n Hero garen = new Hero();\n garen.name = \"aaa\";\n System.out.println(\"打印对象看到的虚拟地址:\"+garen);\n garen.showAddressInMemory();\n \n //-------------------------------------------\n Hero teemo = new Hero();\n teemo.name = \"bbb\";\n System.out.println(\"打印对象看到的虚拟地址:\"+teemo);\n teemo.showAddressInMemory();\n } \n \n}\n\n//输出:\n/*\n打印对象看到的虚拟地址:Hero@2a139a55\n打印this看到的虚拟地址:Hero@2a139a55\n打印对象看到的虚拟地址:Hero@15db9742\n打印this看到的虚拟地址:Hero@15db9742\n*/\n```\n\n通过`this` 可以访问对象的属性、方法\n\n通过`this()`可以调用其他的构造方法\n\n```java\npublic class Hero {\n\n String name; // 姓名\n\n float hp; // 血量\n\n float armor; // 护甲\n\n int moveSpeed; // 移动速度\n\n // 带一个参数的构造方法\n public Hero(String name) {\n System.out.println(\"一个参数的构造方法\");\n this.name = name;\n }\n\n // 带两个参数的构造方法\n public Hero(String name, float hp) {\n this(name); \n System.out.println(\"两个参数的构造方法\");\n this.hp = hp;\n }\n\n public static void main(String[] args) {\n Hero teemo = new Hero(\"aaaa\", 383); //两个参数\n\n System.out.println(teemo.name);\n\n }\n\n}\n\n/*输出:\n\n一个参数的构造方法\n两个参数的构造方法\naaaa\n*/\n```\n\n## 传参\n\n- 基本类型传参与引用类型传参\n\n 基本类型传参,不多说\n\n 引用类型传参,即传入参数为引用类型\n\n- `=`的含义\n\n - 如果变量是基本类型,则 `=`表示赋值\n\n - 如果变量是引用类型,`=` 表示指向的意思,比如\n\n ```java\n Hero h = new Hero(); //引用h,指向一个Hero对象\n ```\n\n- 引用类型传参\n\n 形参与实参都指向同一对象\n\n ```java\n public class Hero {\n \n String name; \n \n float hp;\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@2a139a55\n */\n ```\n\n 用图表示如下![image-20210123184909979](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210123184909979.png)\n\n 但如果形参在内部指向了新的对象,实参是不会跟着改变的\n\n ```java\n public class Hero {\n \n String name; // 姓名\n \n float hp; // 血量\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n hero = new Hero(\"hero\", 100); //形参指向了新的对象\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@15db9742\n */\n ```\n\n 用图表示如下![image-20210123185224792](https://gitee.com/ajream/images/raw/master/img/image-20210123185224792.png)\n\n\n\n## 类属性(又叫静态属性)\n\n当一个属性被**`static`**修饰的时候,就叫做**类属性**,又叫做**静态属性**\n当一个属性被声明成类属性,那么**所有的对象,都共享一个值**\n\n**对象属性:** 又叫实例属性,非静态属性\n\n> 与对象属性对比:\n> 不同对象的 对象属性 的值都可能不一样。\n\n- 访问类属性:\n\n ```java\n 类.类属性\n ```\n\n ```java\n //访问对象属性:\n 对象.对象属性\n ```\n\n- 类属性与对象属性的选择\n\n - 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是**跟着对象走的**,每个对象的name都是不同的\n - 如果一个属性,**所有的英雄都共享**,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性\n\n\n\n## 类方法(静态方法)\n\n**类方法:** 又叫做静态方法,即被`static`修饰的方法\n\n**对象方法:** 又叫实例方法,非静态方法\n\n> 区别:\n>\n> 调用一个对象方法,必须**建立在有一个对象**的前提的基础上\n> 调用类方法,**不需要对象**的存在,直接就访问\n\n- 调用类方法\n\n ```java\n 类.类方法\n ```\n\n ```java\n //访问对象方法\n 对象.对象方法\n ```\n\n- 什么时候用类方法\n\n 如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如:\n\n ```java\n public static void printGameDuration(){\n \tSystem.out.println(\"已经玩了50秒\");\n }\n ```\n\n 反之,如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法;\n\n 注意:类方法中不能直接调用对象方法\n\n---\n\n[回到开头](#1-类与对象)\n\n","source":"_posts/javaSE/1-类与对象.md","raw":"---\ntitle: 1-类与对象\ntags:\n - javaSE\ncategories:\n - java\n - javaSE\nabbrlink: 52b44ec7\ndate: 2021-02-05 13:12:19\ndescription: 关于Java的类的基本知识,例如类的属性、方法(传参及调用)、继承、this关键字等\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n\n# 类与对象\n\n类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;\n\n用代码创建一个鸟类:\n\n```java\npublic class Bird{\n String name;\n String feathers_color;\n public void fly(){\n System.out.println(name + \"可以飞\")\n }\n}\n```\n\n对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;\n\n用代码来创建对象:\n\n```java\nBird bird1 = new Bird();\nBird bird2 = new Bird();\n```\n\n## 引用与指向\n\n在下面创建的这一对象中\n\n```java\nBird bird1 = new Bird();\n```\n\n`bird1`存储的是对象的地址,因此`bird1`是对象的**引用**;这一地址**指向**了对象本身;\n\n\"image-20210123153305198\"\n\n- 多个引用指向同一对象\n\n```java\nBird b1 = new Bird();\nBird b2 = b1;\nBird b3 = b1;\n```\n\n\"image-20210123154400728\"\n\n- 多个引用指向多个(不同)对象\n\n```java\nBird b1 = new Bird();\nBird b2 = new Bird();\nBird b3 = new Bird();\n```\n\n\"image-20210123154635298\"\n\n## 继承\n\n可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:\n\n```java\npublic class ClassName2 extends ClassName1{\n\n}\n```\n\n例如:\n\n首先创建父类Item\n\n```java\npublic class Item {\n String name;\n int price;\n}\n```\n\n创建子类Weapon继承Item\n\n```java\npublic class Weapon extends Item{\n int damage; //攻击力\n \n public static void main(String[] args) {\n Weapon infinityEdge = new Weapon();\n infinityEdge.damage = 65; //damage属性在类Weapon中新设计的\n \n infinityEdge.name = \"无尽之刃\";//name属性,是从Item中继承来的,就不需要重复设计了\n infinityEdge.price = 3600;\n \n }\n \n}\n```\n\n## 方法重载\n\n方法的重载指的是**方法名一样**,但是参数类型或数量不一样\n\n```java\npublic class ADHero extends Hero {\n //无参数\n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n \t//1个参数\n public void attack(Hero h1) {\n System.out.println(name + \"对\" + h1.name + \"进行了一次攻击 \");\n }\n \t\n //2个参数\n public void attack(Hero h1, Hero h2) {\n System.out.println(name + \"同时对\" + h1.name + \"和\" + h2.name + \"进行了攻击 \");\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n }\n \n}\n```\n\n可变数量的参数\n\n采用可变数量的参数 **只需要设计一个方法**:\n`public void attack(Hero... heros)`\n即可代表上述所有的方法了,此时 `heros`被当作一个**数组**,在方法里,使用操作数组的方式处理参数heros即可\n\n```java\npublic class ADHero extends Hero {\n \n public void attack() {\n System.out.println(name + \" 进行了一次攻击 ,但是不确定打中谁了\");\n }\n \n // 可变数量的参数\n public void attack(Hero... heros) {\n for (int i = 0; i < heros.length; i++) {\n System.out.println(name + \" 攻击了 \" + heros[i].name);\n \n }\n }\n \n public static void main(String[] args) {\n ADHero bh = new ADHero();\n bh.name = \"赏金猎人\";\n \n Hero h1 = new Hero();\n h1.name = \"盖伦\";\n Hero h2 = new Hero();\n h2.name = \"提莫\";\n \n bh.attack(h1);\n bh.attack(h1, h2);\n \n }\n \n}\n```\n\n## 构造方法\n\n构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法\n\n```java\npublic class Hero {\n \n String name;\n \n float hp;\n \n float armor;\n \n int moveSpeed;\n \n // 方法名和类名一样(包括大小写)\n // 没有返回类型\n public Hero() {\n System.out.println(\"实例化一个对象的时候,必然调用构造方法\");\n }\n \n public static void main(String[] args) {\n //实例化一个对象的时候,必然调用构造方法\n Hero h = new Hero();\n }\n \n}\n\n//输出:实例化一个对象的时候,必然调用构造方法\n```\n\n构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同\n\n## this\n\n`this` 代表**当前对象**,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有\n\n```java\npublic class Hero {\n \n String name; //姓名\n \n float hp; //血量\n \n float armor; //护甲\n \n int moveSpeed; //移动速度\n \n //打印内存中的虚拟地址\n public void showAddressInMemory(){\n System.out.println(\"打印this看到的虚拟地址:\"+this);\n }\n \n public static void main(String[] args) {\n //---------------------------------------------\n Hero garen = new Hero();\n garen.name = \"aaa\";\n System.out.println(\"打印对象看到的虚拟地址:\"+garen);\n garen.showAddressInMemory();\n \n //-------------------------------------------\n Hero teemo = new Hero();\n teemo.name = \"bbb\";\n System.out.println(\"打印对象看到的虚拟地址:\"+teemo);\n teemo.showAddressInMemory();\n } \n \n}\n\n//输出:\n/*\n打印对象看到的虚拟地址:Hero@2a139a55\n打印this看到的虚拟地址:Hero@2a139a55\n打印对象看到的虚拟地址:Hero@15db9742\n打印this看到的虚拟地址:Hero@15db9742\n*/\n```\n\n通过`this` 可以访问对象的属性、方法\n\n通过`this()`可以调用其他的构造方法\n\n```java\npublic class Hero {\n\n String name; // 姓名\n\n float hp; // 血量\n\n float armor; // 护甲\n\n int moveSpeed; // 移动速度\n\n // 带一个参数的构造方法\n public Hero(String name) {\n System.out.println(\"一个参数的构造方法\");\n this.name = name;\n }\n\n // 带两个参数的构造方法\n public Hero(String name, float hp) {\n this(name); \n System.out.println(\"两个参数的构造方法\");\n this.hp = hp;\n }\n\n public static void main(String[] args) {\n Hero teemo = new Hero(\"aaaa\", 383); //两个参数\n\n System.out.println(teemo.name);\n\n }\n\n}\n\n/*输出:\n\n一个参数的构造方法\n两个参数的构造方法\naaaa\n*/\n```\n\n## 传参\n\n- 基本类型传参与引用类型传参\n\n 基本类型传参,不多说\n\n 引用类型传参,即传入参数为引用类型\n\n- `=`的含义\n\n - 如果变量是基本类型,则 `=`表示赋值\n\n - 如果变量是引用类型,`=` 表示指向的意思,比如\n\n ```java\n Hero h = new Hero(); //引用h,指向一个Hero对象\n ```\n\n- 引用类型传参\n\n 形参与实参都指向同一对象\n\n ```java\n public class Hero {\n \n String name; \n \n float hp;\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@2a139a55\n */\n ```\n\n 用图表示如下![image-20210123184909979](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210123184909979.png)\n\n 但如果形参在内部指向了新的对象,实参是不会跟着改变的\n\n ```java\n public class Hero {\n \n String name; // 姓名\n \n float hp; // 血量\n \n public Hero(String name, float hp) {\n this.name = name;\n this.hp = hp;\n }\n \n public void attack(Hero hero) {\n hero = new Hero(\"hero\", 100); //形参指向了新的对象\n System.out.println(\"hero:\"+hero);\n }\n \n public static void main(String[] args) {\n Hero teemo = new Hero(\"teemo\", 383);\n Hero garen = new Hero(\"garen\", 616);\n garen.attack(teemo);\n System.out.println(\"teemo:\"+teemo);\n \n }\n \n }\n \n /**输出:\n hero:Hero@2a139a55\n teemo:Hero@15db9742\n */\n ```\n\n 用图表示如下![image-20210123185224792](https://gitee.com/ajream/images/raw/master/img/image-20210123185224792.png)\n\n\n\n## 类属性(又叫静态属性)\n\n当一个属性被**`static`**修饰的时候,就叫做**类属性**,又叫做**静态属性**\n当一个属性被声明成类属性,那么**所有的对象,都共享一个值**\n\n**对象属性:** 又叫实例属性,非静态属性\n\n> 与对象属性对比:\n> 不同对象的 对象属性 的值都可能不一样。\n\n- 访问类属性:\n\n ```java\n 类.类属性\n ```\n\n ```java\n //访问对象属性:\n 对象.对象属性\n ```\n\n- 类属性与对象属性的选择\n\n - 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是**跟着对象走的**,每个对象的name都是不同的\n - 如果一个属性,**所有的英雄都共享**,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性\n\n\n\n## 类方法(静态方法)\n\n**类方法:** 又叫做静态方法,即被`static`修饰的方法\n\n**对象方法:** 又叫实例方法,非静态方法\n\n> 区别:\n>\n> 调用一个对象方法,必须**建立在有一个对象**的前提的基础上\n> 调用类方法,**不需要对象**的存在,直接就访问\n\n- 调用类方法\n\n ```java\n 类.类方法\n ```\n\n ```java\n //访问对象方法\n 对象.对象方法\n ```\n\n- 什么时候用类方法\n\n 如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如:\n\n ```java\n public static void printGameDuration(){\n \tSystem.out.println(\"已经玩了50秒\");\n }\n ```\n\n 反之,如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法;\n\n 注意:类方法中不能直接调用对象方法\n\n---\n\n[回到开头](#1-类与对象)\n\n","slug":"javaSE/1-类与对象","published":1,"updated":"2021-09-10T03:05:35.295Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qj005dakve37dy5lne","content":"

类与对象

类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;

\n

用代码创建一个鸟类:

\n
1
2
3
4
5
6
7
public class Bird{
String name;
String feathers_color;
public void fly(){
System.out.println(name + "可以飞")
}
}
\n

对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;

\n

用代码来创建对象:

\n
1
2
Bird bird1 = new Bird();
Bird bird2 = new Bird();
\n

引用与指向

在下面创建的这一对象中

\n
1
Bird bird1 = new Bird();
\n

bird1存储的是对象的地址,因此bird1是对象的引用;这一地址指向了对象本身;

\n

\"image-20210123153305198\"

\n
    \n
  • 多个引用指向同一对象
  • \n
\n
1
2
3
Bird b1 = new Bird();
Bird b2 = b1;
Bird b3 = b1;
\n

\"image-20210123154400728\"

\n
    \n
  • 多个引用指向多个(不同)对象
  • \n
\n
1
2
3
Bird b1 = new Bird();
Bird b2 = new Bird();
Bird b3 = new Bird();
\n

\"image-20210123154635298\"

\n

继承

可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:

\n
1
2
3
public class ClassName2 extends ClassName1{

}
\n

例如:

\n

首先创建父类Item

\n
1
2
3
4
public class Item {
String name;
int price;
}
\n

创建子类Weapon继承Item

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Weapon extends Item{
int damage; //攻击力

public static void main(String[] args) {
Weapon infinityEdge = new Weapon();
infinityEdge.damage = 65; //damage属性在类Weapon中新设计的

infinityEdge.name = "无尽之刃";//name属性,是从Item中继承来的,就不需要重复设计了
infinityEdge.price = 3600;

}

}
\n

方法重载

方法的重载指的是方法名一样,但是参数类型或数量不一样

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ADHero extends Hero {
//无参数
public void attack() {
System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
}

\t//1个参数
public void attack(Hero h1) {
System.out.println(name + "对" + h1.name + "进行了一次攻击 ");
}
\t
//2个参数
public void attack(Hero h1, Hero h2) {
System.out.println(name + "同时对" + h1.name + "和" + h2.name + "进行了攻击 ");
}

public static void main(String[] args) {
ADHero bh = new ADHero();
bh.name = "赏金猎人";

Hero h1 = new Hero();
h1.name = "盖伦";
Hero h2 = new Hero();
h2.name = "提莫";

bh.attack(h1);
bh.attack(h1, h2);
}

}
\n

可变数量的参数

\n

采用可变数量的参数 只需要设计一个方法
public void attack(Hero... heros)
即可代表上述所有的方法了,此时 heros被当作一个数组,在方法里,使用操作数组的方式处理参数heros即可

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ADHero extends Hero {

public void attack() {
System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
}

// 可变数量的参数
public void attack(Hero... heros) {
for (int i = 0; i < heros.length; i++) {
System.out.println(name + " 攻击了 " + heros[i].name);

}
}

public static void main(String[] args) {
ADHero bh = new ADHero();
bh.name = "赏金猎人";

Hero h1 = new Hero();
h1.name = "盖伦";
Hero h2 = new Hero();
h2.name = "提莫";

bh.attack(h1);
bh.attack(h1, h2);

}

}
\n

构造方法

构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Hero {

String name;

float hp;

float armor;

int moveSpeed;

// 方法名和类名一样(包括大小写)
// 没有返回类型
public Hero() {
System.out.println("实例化一个对象的时候,必然调用构造方法");
}

public static void main(String[] args) {
//实例化一个对象的时候,必然调用构造方法
Hero h = new Hero();
}

}

//输出:实例化一个对象的时候,必然调用构造方法
\n

构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同

\n

this

this 代表当前对象,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Hero {

String name; //姓名

float hp; //血量

float armor; //护甲

int moveSpeed; //移动速度

//打印内存中的虚拟地址
public void showAddressInMemory(){
System.out.println("打印this看到的虚拟地址:"+this);
}

public static void main(String[] args) {
//---------------------------------------------
Hero garen = new Hero();
garen.name = "aaa";
System.out.println("打印对象看到的虚拟地址:"+garen);
garen.showAddressInMemory();

//-------------------------------------------
Hero teemo = new Hero();
teemo.name = "bbb";
System.out.println("打印对象看到的虚拟地址:"+teemo);
teemo.showAddressInMemory();
}

}

//输出:
/*
打印对象看到的虚拟地址:Hero@2a139a55
打印this看到的虚拟地址:Hero@2a139a55
打印对象看到的虚拟地址:Hero@15db9742
打印this看到的虚拟地址:Hero@15db9742
*/
\n

通过this 可以访问对象的属性、方法

\n

通过this()可以调用其他的构造方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Hero {

String name; // 姓名

float hp; // 血量

float armor; // 护甲

int moveSpeed; // 移动速度

// 带一个参数的构造方法
public Hero(String name) {
System.out.println("一个参数的构造方法");
this.name = name;
}

// 带两个参数的构造方法
public Hero(String name, float hp) {
this(name);
System.out.println("两个参数的构造方法");
this.hp = hp;
}

public static void main(String[] args) {
Hero teemo = new Hero("aaaa", 383); //两个参数

System.out.println(teemo.name);

}

}

/*输出:

一个参数的构造方法
两个参数的构造方法
aaaa
*/
\n

传参

    \n
  • 基本类型传参与引用类型传参

    \n

    基本类型传参,不多说

    \n

    引用类型传参,即传入参数为引用类型

    \n
  • \n
  • =的含义

    \n
      \n
    • 如果变量是基本类型,则 =表示赋值

      \n
    • \n
    • 如果变量是引用类型,= 表示指向的意思,比如

      \n
      1
      Hero h = new Hero();  //引用h,指向一个Hero对象
      \n
    • \n
    \n
  • \n
  • 引用类型传参

    \n

    形参与实参都指向同一对象

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Hero {

    String name;

    float hp;

    public Hero(String name, float hp) {
    this.name = name;
    this.hp = hp;
    }

    public void attack(Hero hero) {
    System.out.println("hero:"+hero);
    }

    public static void main(String[] args) {
    Hero teemo = new Hero("teemo", 383);
    Hero garen = new Hero("garen", 616);
    garen.attack(teemo);
    System.out.println("teemo:"+teemo);

    }

    }

    /**输出:
    hero:Hero@2a139a55
    teemo:Hero@2a139a55
    */
    \n

    用图表示如下\"image-20210123184909979\"

    \n

    但如果形参在内部指向了新的对象,实参是不会跟着改变的

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Hero {

    String name; // 姓名

    float hp; // 血量

    public Hero(String name, float hp) {
    this.name = name;
    this.hp = hp;
    }

    public void attack(Hero hero) {
    hero = new Hero("hero", 100); //形参指向了新的对象
    System.out.println("hero:"+hero);
    }

    public static void main(String[] args) {
    Hero teemo = new Hero("teemo", 383);
    Hero garen = new Hero("garen", 616);
    garen.attack(teemo);
    System.out.println("teemo:"+teemo);

    }

    }

    /**输出:
    hero:Hero@2a139a55
    teemo:Hero@15db9742
    */
    \n

    用图表示如下\"image-20210123185224792\"

    \n
  • \n
\n

类属性(又叫静态属性)

当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性
当一个属性被声明成类属性,那么所有的对象,都共享一个值

\n

对象属性: 又叫实例属性,非静态属性

\n
\n

与对象属性对比:
不同对象的 对象属性 的值都可能不一样。

\n
\n
    \n
  • 访问类属性:

    \n
    1
    类.类属性
    \n
    1
    2
    //访问对象属性:
    对象.对象属性
    \n
  • \n
  • 类属性与对象属性的选择

    \n
      \n
    • 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的,每个对象的name都是不同的
    • \n
    • 如果一个属性,所有的英雄都共享,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性
    • \n
    \n
  • \n
\n

类方法(静态方法)

类方法: 又叫做静态方法,即被static修饰的方法

\n

对象方法: 又叫实例方法,非静态方法

\n
\n

区别:

\n

调用一个对象方法,必须建立在有一个对象的前提的基础上
调用类方法,不需要对象的存在,直接就访问

\n
\n
    \n
  • 调用类方法

    \n
    1
    类.类方法
    \n
    1
    2
    //访问对象方法
    对象.对象方法
    \n
  • \n
  • 什么时候用类方法

    \n

    如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如:

    \n
    1
    2
    3
    public static void printGameDuration(){
    \tSystem.out.println("已经玩了50秒");
    }
    \n

    反之,如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法;

    \n

    注意:类方法中不能直接调用对象方法

    \n
  • \n
\n
\n

回到开头

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

类与对象

类可以看成是具有很多相似性质、功能的事物的集合,如鸟可以看作一个类,鸟都具有各自的名称,各色的羽毛、飞等相似特点;

\n

用代码创建一个鸟类:

\n
1
2
3
4
5
6
7
public class Bird{
String name;
String feathers_color;
public void fly(){
System.out.println(name + "可以飞")
}
}
\n

对象可以看作是类的一个具体事例,如上面所说的鸟类,鸽子是鸟类的一种,老鹰也是鸟类一种,因此鸽子、老鹰都可以分别看作是鸟类的一个对象;

\n

用代码来创建对象:

\n
1
2
Bird bird1 = new Bird();
Bird bird2 = new Bird();
\n

引用与指向

在下面创建的这一对象中

\n
1
Bird bird1 = new Bird();
\n

bird1存储的是对象的地址,因此bird1是对象的引用;这一地址指向了对象本身;

\n

\"image-20210123153305198\"

\n
    \n
  • 多个引用指向同一对象
  • \n
\n
1
2
3
Bird b1 = new Bird();
Bird b2 = b1;
Bird b3 = b1;
\n

\"image-20210123154400728\"

\n
    \n
  • 多个引用指向多个(不同)对象
  • \n
\n
1
2
3
Bird b1 = new Bird();
Bird b2 = new Bird();
Bird b3 = new Bird();
\n

\"image-20210123154635298\"

\n

继承

可以创建两个类,其中一个类可以继承另一个类的属性和方法,继承语法:

\n
1
2
3
public class ClassName2 extends ClassName1{

}
\n

例如:

\n

首先创建父类Item

\n
1
2
3
4
public class Item {
String name;
int price;
}
\n

创建子类Weapon继承Item

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Weapon extends Item{
int damage; //攻击力

public static void main(String[] args) {
Weapon infinityEdge = new Weapon();
infinityEdge.damage = 65; //damage属性在类Weapon中新设计的

infinityEdge.name = "无尽之刃";//name属性,是从Item中继承来的,就不需要重复设计了
infinityEdge.price = 3600;

}

}
\n

方法重载

方法的重载指的是方法名一样,但是参数类型或数量不一样

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ADHero extends Hero {
//无参数
public void attack() {
System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
}

\t//1个参数
public void attack(Hero h1) {
System.out.println(name + "对" + h1.name + "进行了一次攻击 ");
}
\t
//2个参数
public void attack(Hero h1, Hero h2) {
System.out.println(name + "同时对" + h1.name + "和" + h2.name + "进行了攻击 ");
}

public static void main(String[] args) {
ADHero bh = new ADHero();
bh.name = "赏金猎人";

Hero h1 = new Hero();
h1.name = "盖伦";
Hero h2 = new Hero();
h2.name = "提莫";

bh.attack(h1);
bh.attack(h1, h2);
}

}
\n

可变数量的参数

\n

采用可变数量的参数 只需要设计一个方法
public void attack(Hero... heros)
即可代表上述所有的方法了,此时 heros被当作一个数组,在方法里,使用操作数组的方式处理参数heros即可

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ADHero extends Hero {

public void attack() {
System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
}

// 可变数量的参数
public void attack(Hero... heros) {
for (int i = 0; i < heros.length; i++) {
System.out.println(name + " 攻击了 " + heros[i].name);

}
}

public static void main(String[] args) {
ADHero bh = new ADHero();
bh.name = "赏金猎人";

Hero h1 = new Hero();
h1.name = "盖伦";
Hero h2 = new Hero();
h2.name = "提莫";

bh.attack(h1);
bh.attack(h1, h2);

}

}
\n

构造方法

构造方法是一个特殊的方法,其方法名与类名一样,没有返回类型,在实例化一个对象时,必然调用构造方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Hero {

String name;

float hp;

float armor;

int moveSpeed;

// 方法名和类名一样(包括大小写)
// 没有返回类型
public Hero() {
System.out.println("实例化一个对象的时候,必然调用构造方法");
}

public static void main(String[] args) {
//实例化一个对象的时候,必然调用构造方法
Hero h = new Hero();
}

}

//输出:实例化一个对象的时候,必然调用构造方法
\n

构造方法也可以重载,即可以有几个构造方法,但它们的参数数量或类型不同

\n

this

this 代表当前对象,因此把this看成一个对象就行,对象的属性、方法、构造方法它都有

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Hero {

String name; //姓名

float hp; //血量

float armor; //护甲

int moveSpeed; //移动速度

//打印内存中的虚拟地址
public void showAddressInMemory(){
System.out.println("打印this看到的虚拟地址:"+this);
}

public static void main(String[] args) {
//---------------------------------------------
Hero garen = new Hero();
garen.name = "aaa";
System.out.println("打印对象看到的虚拟地址:"+garen);
garen.showAddressInMemory();

//-------------------------------------------
Hero teemo = new Hero();
teemo.name = "bbb";
System.out.println("打印对象看到的虚拟地址:"+teemo);
teemo.showAddressInMemory();
}

}

//输出:
/*
打印对象看到的虚拟地址:Hero@2a139a55
打印this看到的虚拟地址:Hero@2a139a55
打印对象看到的虚拟地址:Hero@15db9742
打印this看到的虚拟地址:Hero@15db9742
*/
\n

通过this 可以访问对象的属性、方法

\n

通过this()可以调用其他的构造方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Hero {

String name; // 姓名

float hp; // 血量

float armor; // 护甲

int moveSpeed; // 移动速度

// 带一个参数的构造方法
public Hero(String name) {
System.out.println("一个参数的构造方法");
this.name = name;
}

// 带两个参数的构造方法
public Hero(String name, float hp) {
this(name);
System.out.println("两个参数的构造方法");
this.hp = hp;
}

public static void main(String[] args) {
Hero teemo = new Hero("aaaa", 383); //两个参数

System.out.println(teemo.name);

}

}

/*输出:

一个参数的构造方法
两个参数的构造方法
aaaa
*/
\n

传参

    \n
  • 基本类型传参与引用类型传参

    \n

    基本类型传参,不多说

    \n

    引用类型传参,即传入参数为引用类型

    \n
  • \n
  • =的含义

    \n
      \n
    • 如果变量是基本类型,则 =表示赋值

      \n
    • \n
    • 如果变量是引用类型,= 表示指向的意思,比如

      \n
      1
      Hero h = new Hero();  //引用h,指向一个Hero对象
      \n
    • \n
    \n
  • \n
  • 引用类型传参

    \n

    形参与实参都指向同一对象

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Hero {

    String name;

    float hp;

    public Hero(String name, float hp) {
    this.name = name;
    this.hp = hp;
    }

    public void attack(Hero hero) {
    System.out.println("hero:"+hero);
    }

    public static void main(String[] args) {
    Hero teemo = new Hero("teemo", 383);
    Hero garen = new Hero("garen", 616);
    garen.attack(teemo);
    System.out.println("teemo:"+teemo);

    }

    }

    /**输出:
    hero:Hero@2a139a55
    teemo:Hero@2a139a55
    */
    \n

    用图表示如下\"image-20210123184909979\"

    \n

    但如果形参在内部指向了新的对象,实参是不会跟着改变的

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Hero {

    String name; // 姓名

    float hp; // 血量

    public Hero(String name, float hp) {
    this.name = name;
    this.hp = hp;
    }

    public void attack(Hero hero) {
    hero = new Hero("hero", 100); //形参指向了新的对象
    System.out.println("hero:"+hero);
    }

    public static void main(String[] args) {
    Hero teemo = new Hero("teemo", 383);
    Hero garen = new Hero("garen", 616);
    garen.attack(teemo);
    System.out.println("teemo:"+teemo);

    }

    }

    /**输出:
    hero:Hero@2a139a55
    teemo:Hero@15db9742
    */
    \n

    用图表示如下\"image-20210123185224792\"

    \n
  • \n
\n

类属性(又叫静态属性)

当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性
当一个属性被声明成类属性,那么所有的对象,都共享一个值

\n

对象属性: 又叫实例属性,非静态属性

\n
\n

与对象属性对比:
不同对象的 对象属性 的值都可能不一样。

\n
\n
    \n
  • 访问类属性:

    \n
    1
    类.类属性
    \n
    1
    2
    //访问对象属性:
    对象.对象属性
    \n
  • \n
  • 类属性与对象属性的选择

    \n
      \n
    • 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的,每个对象的name都是不同的
    • \n
    • 如果一个属性,所有的英雄都共享,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性
    • \n
    \n
  • \n
\n

类方法(静态方法)

类方法: 又叫做静态方法,即被static修饰的方法

\n

对象方法: 又叫实例方法,非静态方法

\n
\n

区别:

\n

调用一个对象方法,必须建立在有一个对象的前提的基础上
调用类方法,不需要对象的存在,直接就访问

\n
\n
    \n
  • 调用类方法

    \n
    1
    类.类方法
    \n
    1
    2
    //访问对象方法
    对象.对象方法
    \n
  • \n
  • 什么时候用类方法

    \n

    如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如:

    \n
    1
    2
    3
    public static void printGameDuration(){
    \tSystem.out.println("已经玩了50秒");
    }
    \n

    反之,如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法;

    \n

    注意:类方法中不能直接调用对象方法

    \n
  • \n
\n
\n

回到开头

\n"},{"title":"Http协议","description":"http协议介绍与工作原理","abbrlink":"374aa28c","date":"2021-04-16T06:31:33.000Z","_content":"\n\n\n\n\n\n\n## 介绍\n\n\n\n超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在[TCP](https://baike.baidu.com/item/TCP/33012)之上,如图\n\n![image-20210903214830491](https://gitee.com/ajream/images/raw/master/img/20210903214835_image-20210903214830491.png)\n\n\n\n文本:html, 字符串......\n\n超文本:图片、音乐……\n\nhttp端口:80\n\nhttps端口:443\n\n\n\n## Http请求与响应\n\nHTTP协议永远都是客户端发起请求,服务器回送响应。见下图:\n\n![image-20210903214905388](https://gitee.com/ajream/images/raw/master/img/20210903214907_image-20210903214905388.png)\n\n这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端\n\n\n\n请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体\n\n响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体\n\n\n\n**HTTP请求方式**\n\nGET:最常用的请求方式,使用GET方法应该只用在获取数据\n\nPOST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有\n\n除此以外还有`HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT` 这8种不常用的请求方式\n\n\n\n**URL**\n\nURL被称为:统一资源定位符\n\n其中通常包含信息:\n\n- 传送协议\n- 层级URL标记符号(为[//],固定不变)\n- 访问资源需要的凭证信息(可省略)\n- 服务器地址(通常为域名,有时为IP地址)\n- 端口号(可省略,默认使用http协议,端口为80)\n- 资源路径(以“/”字符区别路径中的每一个目录名称)\n- 查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)\n- 片段。以“#”字符为起点(可省略)\n\n例如:\n\n```\nhttp://www.luffycity.com:80/news/index.html?id=250&page=1\n```\n\n\n\n**HTTP状态码**\n\n状态码元由3位数字组成,表示请求是否被理解或被满足。\n\n\n\n1XX: 信息——请求已被服务器接收,继续处理\n\n2XX: 连接成功\n\n- 200:请求成功(其后是对GET和POST请求的应答文档)\n\n3XX: 重定向\n\n- 300:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址\n\n- 301:所请求的页面已经转移至新的url\n\n- 302:所请求的页面已经临时转移至新的url\n\n- 303:所请求的页面可在别的url下被找到\n\n \n\n4XX: 请求错误\n\n- 400:服务器不理解请求\n- 401:被请求的页面需要用户名和密码\n- 403:对被请求页面的访问被禁止\n- 404:服务器无法找到被请求的页面\n\n5XX: 服务器错误\n\n- 500:请求未完成,服务器遇到不可预知的情况。\n- 501:请求未完成。服务器不支持所请求的功能。\n- 503:请求未完成。服务器临时过载或宕机。\n- 504:网关超时\n\n\n\n\n\n**HTTP请求报文**\n\n\"image-20210903214930366\"\n\n例如:\n\n\"image-20210903214949259\"\n\n\n\n**HTTP响应报文**\n\n\"image-20210903215042801\"\n\n\n\n例如:\n\n\"image-20210903215124843\"\n\n\n\n## Http工作原理\n\nHTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。\n\nHTTP协议采用了请求/响应模型:\n\n- 客户端向服务器发送一个【请求报文】,请求报文包含请求的方法(get/post/...)、URL、协议版本、请求头部和请求数据。\n\n- 服务器以一个【状态行】作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。\n\n典型的HTTP事务处理有如下的过程: \n\n(1)客户与服务器建立连接:\n\n一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接\n\n(2)客户向服务器提出请求:\n\n通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。\n\n(3)服务器接受请求,并根据请求返回相应的文件作为应答:\n\nWeb服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。\n\n(4)客户与服务器关闭连接:\n\n- 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;\n\n- 若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;\n\n(5)客户端浏览器解析HTML内容:\n\n客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。\n\n然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。\n\n客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。\n\n","source":"_posts/javaEE/http.md","raw":"---\ntitle: Http协议\ntags:\n - Http\ncategories:\n - - java\n - javaEE\ndescription: http协议介绍与工作原理\nabbrlink: 374aa28c\ndate: 2021-04-16 14:31:33\n---\n\n\n\n\n\n\n\n## 介绍\n\n\n\n超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在[TCP](https://baike.baidu.com/item/TCP/33012)之上,如图\n\n![image-20210903214830491](https://gitee.com/ajream/images/raw/master/img/20210903214835_image-20210903214830491.png)\n\n\n\n文本:html, 字符串......\n\n超文本:图片、音乐……\n\nhttp端口:80\n\nhttps端口:443\n\n\n\n## Http请求与响应\n\nHTTP协议永远都是客户端发起请求,服务器回送响应。见下图:\n\n![image-20210903214905388](https://gitee.com/ajream/images/raw/master/img/20210903214907_image-20210903214905388.png)\n\n这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端\n\n\n\n请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体\n\n响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体\n\n\n\n**HTTP请求方式**\n\nGET:最常用的请求方式,使用GET方法应该只用在获取数据\n\nPOST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有\n\n除此以外还有`HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT` 这8种不常用的请求方式\n\n\n\n**URL**\n\nURL被称为:统一资源定位符\n\n其中通常包含信息:\n\n- 传送协议\n- 层级URL标记符号(为[//],固定不变)\n- 访问资源需要的凭证信息(可省略)\n- 服务器地址(通常为域名,有时为IP地址)\n- 端口号(可省略,默认使用http协议,端口为80)\n- 资源路径(以“/”字符区别路径中的每一个目录名称)\n- 查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)\n- 片段。以“#”字符为起点(可省略)\n\n例如:\n\n```\nhttp://www.luffycity.com:80/news/index.html?id=250&page=1\n```\n\n\n\n**HTTP状态码**\n\n状态码元由3位数字组成,表示请求是否被理解或被满足。\n\n\n\n1XX: 信息——请求已被服务器接收,继续处理\n\n2XX: 连接成功\n\n- 200:请求成功(其后是对GET和POST请求的应答文档)\n\n3XX: 重定向\n\n- 300:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址\n\n- 301:所请求的页面已经转移至新的url\n\n- 302:所请求的页面已经临时转移至新的url\n\n- 303:所请求的页面可在别的url下被找到\n\n \n\n4XX: 请求错误\n\n- 400:服务器不理解请求\n- 401:被请求的页面需要用户名和密码\n- 403:对被请求页面的访问被禁止\n- 404:服务器无法找到被请求的页面\n\n5XX: 服务器错误\n\n- 500:请求未完成,服务器遇到不可预知的情况。\n- 501:请求未完成。服务器不支持所请求的功能。\n- 503:请求未完成。服务器临时过载或宕机。\n- 504:网关超时\n\n\n\n\n\n**HTTP请求报文**\n\n\"image-20210903214930366\"\n\n例如:\n\n\"image-20210903214949259\"\n\n\n\n**HTTP响应报文**\n\n\"image-20210903215042801\"\n\n\n\n例如:\n\n\"image-20210903215124843\"\n\n\n\n## Http工作原理\n\nHTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。\n\nHTTP协议采用了请求/响应模型:\n\n- 客户端向服务器发送一个【请求报文】,请求报文包含请求的方法(get/post/...)、URL、协议版本、请求头部和请求数据。\n\n- 服务器以一个【状态行】作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。\n\n典型的HTTP事务处理有如下的过程: \n\n(1)客户与服务器建立连接:\n\n一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接\n\n(2)客户向服务器提出请求:\n\n通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。\n\n(3)服务器接受请求,并根据请求返回相应的文件作为应答:\n\nWeb服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。\n\n(4)客户与服务器关闭连接:\n\n- 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;\n\n- 若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;\n\n(5)客户端浏览器解析HTML内容:\n\n客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。\n\n然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。\n\n客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。\n\n","slug":"javaEE/http","published":1,"updated":"2021-09-03T13:53:35.404Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qj005fakvegpvj86dz","content":"

介绍

超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上,如图

\n

\"image-20210903214830491\"

\n

文本:html, 字符串……

\n

超文本:图片、音乐……

\n

http端口:80

\n

https端口:443

\n

Http请求与响应

HTTP协议永远都是客户端发起请求,服务器回送响应。见下图:

\n

\"image-20210903214905388\"

\n

这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端

\n

请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体

\n

响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体

\n

HTTP请求方式

\n

GET:最常用的请求方式,使用GET方法应该只用在获取数据

\n

POST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有

\n

除此以外还有HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT 这8种不常用的请求方式

\n

URL

\n

URL被称为:统一资源定位符

\n

其中通常包含信息:

\n
    \n
  • 传送协议
  • \n
  • 层级URL标记符号(为[//],固定不变)
  • \n
  • 访问资源需要的凭证信息(可省略)
  • \n
  • 服务器地址(通常为域名,有时为IP地址)
  • \n
  • 端口号(可省略,默认使用http协议,端口为80)
  • \n
  • 资源路径(以“/”字符区别路径中的每一个目录名称)
  • \n
  • 查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
  • \n
  • 片段。以“#”字符为起点(可省略)
  • \n
\n

例如:

\n
1
http://www.luffycity.com:80/news/index.html?id=250&page=1
\n

HTTP状态码

\n

状态码元由3位数字组成,表示请求是否被理解或被满足。

\n

1XX: 信息——请求已被服务器接收,继续处理

\n

2XX: 连接成功

\n
    \n
  • 200:请求成功(其后是对GET和POST请求的应答文档)
  • \n
\n

3XX: 重定向

\n
    \n
  • 300:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址

    \n
  • \n
  • 301:所请求的页面已经转移至新的url

    \n
  • \n
  • 302:所请求的页面已经临时转移至新的url

    \n
  • \n
  • 303:所请求的页面可在别的url下被找到

    \n
  • \n
\n

4XX: 请求错误

\n
    \n
  • 400:服务器不理解请求
  • \n
  • 401:被请求的页面需要用户名和密码
  • \n
  • 403:对被请求页面的访问被禁止
  • \n
  • 404:服务器无法找到被请求的页面
  • \n
\n

5XX: 服务器错误

\n
    \n
  • 500:请求未完成,服务器遇到不可预知的情况。
  • \n
  • 501:请求未完成。服务器不支持所请求的功能。
  • \n
  • 503:请求未完成。服务器临时过载或宕机。
  • \n
  • 504:网关超时
  • \n
\n

HTTP请求报文

\n

\"image-20210903214930366\"

\n

例如:

\n

\"image-20210903214949259\"

\n

HTTP响应报文

\n

\"image-20210903215042801\"

\n

例如:

\n

\"image-20210903215124843\"

\n

Http工作原理

HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。

\n

HTTP协议采用了请求/响应模型:

\n
    \n
  • 客户端向服务器发送一个【请求报文】,请求报文包含请求的方法(get/post/…)、URL、协议版本、请求头部和请求数据。

    \n
  • \n
  • 服务器以一个【状态行】作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。

    \n
  • \n
\n

典型的HTTP事务处理有如下的过程:

\n

(1)客户与服务器建立连接:

\n

一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接

\n

(2)客户向服务器提出请求:

\n

通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。

\n

(3)服务器接受请求,并根据请求返回相应的文件作为应答:

\n

Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。

\n

(4)客户与服务器关闭连接:

\n
    \n
  • 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;

    \n
  • \n
  • 若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;

    \n
  • \n
\n

(5)客户端浏览器解析HTML内容:

\n

客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。

\n

然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。

\n

客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles.png","excerpt":"","more":"

介绍

超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上,如图

\n

\"image-20210903214830491\"

\n

文本:html, 字符串……

\n

超文本:图片、音乐……

\n

http端口:80

\n

https端口:443

\n

Http请求与响应

HTTP协议永远都是客户端发起请求,服务器回送响应。见下图:

\n

\"image-20210903214905388\"

\n

这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端

\n

请求报文:请求行 - 通用信息头 - 请求头 - 实体头 - 报文主体

\n

响应(应答)报文:状态行 - 通用信息头 - 响应头 - 实体头 - 报文主体

\n

HTTP请求方式

\n

GET:最常用的请求方式,使用GET方法应该只用在获取数据

\n

POST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有

\n

除此以外还有HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT 这8种不常用的请求方式

\n

URL

\n

URL被称为:统一资源定位符

\n

其中通常包含信息:

\n
    \n
  • 传送协议
  • \n
  • 层级URL标记符号(为[//],固定不变)
  • \n
  • 访问资源需要的凭证信息(可省略)
  • \n
  • 服务器地址(通常为域名,有时为IP地址)
  • \n
  • 端口号(可省略,默认使用http协议,端口为80)
  • \n
  • 资源路径(以“/”字符区别路径中的每一个目录名称)
  • \n
  • 查询(可省略,GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
  • \n
  • 片段。以“#”字符为起点(可省略)
  • \n
\n

例如:

\n
1
http://www.luffycity.com:80/news/index.html?id=250&page=1
\n

HTTP状态码

\n

状态码元由3位数字组成,表示请求是否被理解或被满足。

\n

1XX: 信息——请求已被服务器接收,继续处理

\n

2XX: 连接成功

\n
    \n
  • 200:请求成功(其后是对GET和POST请求的应答文档)
  • \n
\n

3XX: 重定向

\n
    \n
  • 300:多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址

    \n
  • \n
  • 301:所请求的页面已经转移至新的url

    \n
  • \n
  • 302:所请求的页面已经临时转移至新的url

    \n
  • \n
  • 303:所请求的页面可在别的url下被找到

    \n
  • \n
\n

4XX: 请求错误

\n
    \n
  • 400:服务器不理解请求
  • \n
  • 401:被请求的页面需要用户名和密码
  • \n
  • 403:对被请求页面的访问被禁止
  • \n
  • 404:服务器无法找到被请求的页面
  • \n
\n

5XX: 服务器错误

\n
    \n
  • 500:请求未完成,服务器遇到不可预知的情况。
  • \n
  • 501:请求未完成。服务器不支持所请求的功能。
  • \n
  • 503:请求未完成。服务器临时过载或宕机。
  • \n
  • 504:网关超时
  • \n
\n

HTTP请求报文

\n

\"image-20210903214930366\"

\n

例如:

\n

\"image-20210903214949259\"

\n

HTTP响应报文

\n

\"image-20210903215042801\"

\n

例如:

\n

\"image-20210903215124843\"

\n

Http工作原理

HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。

\n

HTTP协议采用了请求/响应模型:

\n
    \n
  • 客户端向服务器发送一个【请求报文】,请求报文包含请求的方法(get/post/…)、URL、协议版本、请求头部和请求数据。

    \n
  • \n
  • 服务器以一个【状态行】作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。

    \n
  • \n
\n

典型的HTTP事务处理有如下的过程:

\n

(1)客户与服务器建立连接:

\n

一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接

\n

(2)客户向服务器提出请求:

\n

通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。

\n

(3)服务器接受请求,并根据请求返回相应的文件作为应答:

\n

Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。

\n

(4)客户与服务器关闭连接:

\n
    \n
  • 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;

    \n
  • \n
  • 若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;

    \n
  • \n
\n

(5)客户端浏览器解析HTML内容:

\n

客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。

\n

然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。

\n

客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

\n"},{"title":"11-数字与字符串相互转换","abbrlink":"38b36403","date":"2021-02-13T10:58:18.000Z","description":"java的数字类型与String类型的转换实际依靠封装类里面的方法","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 数字⇿字符串\n\n## 数字转字符串(2种方法)\n\n- 使用String类的静态方法`valueOf`\n- 先把基本类型装箱为对象,然后调用对象的`toString`\n\n```java\nint i = 5;\n \n//方法1\nString str = String.valueOf(i);\n \n//方法2\nInteger it = i;\nString str2 = it.toString();\n```\n\n\n\n## 字符串转数字\n\n- 调用Integer的静态方法`parseInt`\n\n```java\nString str = \"999\";\n \nint i= Integer.parseInt(str);\n```\n\n\n\n","source":"_posts/javaSE/11-数字与字符串相互转换.md","raw":"---\ntitle: 11-数字与字符串相互转换\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 38b36403\ndate: 2021-02-13 18:58:18\ndescription: java的数字类型与String类型的转换实际依靠封装类里面的方法\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 数字⇿字符串\n\n## 数字转字符串(2种方法)\n\n- 使用String类的静态方法`valueOf`\n- 先把基本类型装箱为对象,然后调用对象的`toString`\n\n```java\nint i = 5;\n \n//方法1\nString str = String.valueOf(i);\n \n//方法2\nInteger it = i;\nString str2 = it.toString();\n```\n\n\n\n## 字符串转数字\n\n- 调用Integer的静态方法`parseInt`\n\n```java\nString str = \"999\";\n \nint i= Integer.parseInt(str);\n```\n\n\n\n","slug":"javaSE/11-数字与字符串相互转换","published":1,"updated":"2021-08-29T15:31:24.118Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ql005jakve8xo8cwcv","content":"

数字⇿字符串

数字转字符串(2种方法)

    \n
  • 使用String类的静态方法valueOf
  • \n
  • 先把基本类型装箱为对象,然后调用对象的toString
  • \n
\n
1
2
3
4
5
6
7
8
int i = 5;

//方法1
String str = String.valueOf(i);

//方法2
Integer it = i;
String str2 = it.toString();
\n

字符串转数字

    \n
  • 调用Integer的静态方法parseInt
  • \n
\n
1
2
3
String str = "999";

int i= Integer.parseInt(str);
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

数字⇿字符串

数字转字符串(2种方法)

    \n
  • 使用String类的静态方法valueOf
  • \n
  • 先把基本类型装箱为对象,然后调用对象的toString
  • \n
\n
1
2
3
4
5
6
7
8
int i = 5;

//方法1
String str = String.valueOf(i);

//方法2
Integer it = i;
String str2 = it.toString();
\n

字符串转数字

    \n
  • 调用Integer的静态方法parseInt
  • \n
\n
1
2
3
String str = "999";

int i= Integer.parseInt(str);
\n"},{"title":"Tomcat基础","abbrlink":"530cfffd","date":"2021-06-11T05:31:26.000Z","description":"Web基础知识、服务器、Tomcat介绍、安装与使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210901182628_tomcat.png","_content":"\n# Tomcat了解\n\n\n\n## web概念\n\n1. 软件架构\n - C/S:客户端/服务器端(QQ、360。。。)\n - B/S:浏览器/服务器端(京东、淘宝、网易。。。)\n2. 资源分类\n - 静态资源(所有用户访问得到的结果一样)\n - 动态资源(不同用户访问的结果不同)\n3. 网络通信三要素\n - 协议\n - tcp:安全协议、三次握手、速度较慢\n - udp:不安全、速度快\n - ip:电子设备在网络中唯一标识\n - 端口号\n\n\n\n## 常见web服务器软件\n\n1. webLogic:大型JavaEE服务器,属于Oracle公司,收费\n2. webSphere:IBM公司\n3. JBOSS:JBOSS公司\n4. Tomcat:Apache组织\n\n\n\n## Tomcat介绍\n\n[Tomcat下载](https://tomcat.apache.org/download-80.cgi)\n\n\n\nTomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用[服务器](https://baike.baidu.com/item/服务器),在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应[HTML](https://baike.baidu.com/item/HTML)页面的访问请求。\n\n实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。\n\n\n\n### 目录结构\n\n![image-20210903200423036](https://gitee.com/ajream/images/raw/master/img/20210903200427_image-20210903200423036.png)\n\n\n\n重点,服务器配置文件 `/conf/server.xml`\n\n配置访问端口号,默认是 `8080`,如果被占用了可以改为其它端口\n\n```xml\n\n```\n\n配置服务器地址\n\nlocalhost: 实际是指向域名 `127.0.0.1`\n\n```xml\n\n```\n\n{%note primary flat%}\n\n面试题:访问一个网站的流程?\n\n大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)\n\n1. 浏览器输入域名,回车\n2. 浏览器查找本地 `C:\\Windows\\System32\\drivers\\etc\\host`文件,进行域名解析\n3. 如果没有找到,则去DNS服务器查找\n\n{%endnote%}\n\n\n\n## Tomcat使用\n\n\n\n### 启动tomcat\n\n运行bin目录下的批处理文件 `startup.bat`(确认当前是jdk1.8或以上,用`java -version`查看)\n\n\n\n### 部署网页\n\n例如部署一个test.html文件\n\n1. 把test.html文件复制到**D:\\tomcat\\webapps\\ROOT** 目录下\n2. 启动tomcat\n3. 访问 http://127.0.0.1:8080/test.html\n\n\n\n### 改端口号\n\n8080是tomcat默认端口号,而平时上网默认使用80端口\n\ntomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。\n\n修改端口号步骤:\n\n1. 打开server.xml文件\n2. `ctrl+f`查询8080,将下面这句代码的`8080`修改为其他端口号,如80,8081,8082\n\n```xml\n\n```\n\n接着就可以直接通过 http://127.0.0.1/test.html访问网页了。\n\n80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。\n\n\n\n### 端口被占用\n\n端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可\n\n- 查看80端口被哪些程序占用\n\n ```shell\n netstat -ano|findstr \"80\"\n ```\n\n ![image-20210408072223224](https://gitee.com/ajream/images/raw/master/img/image-20210408072223224.png)\n\n 第一行就是占用端口“80”的进程(其他只是含有80这个字符串而已),最后一列对应的进程的pid\n\n- 根据pid查询对应应用程序\n\n ```sh\n tasklist|findstr \"4\"\n ```\n\n ![image-20210408072728546](https://gitee.com/ajream/images/raw/master/img/image-20210408072728546.png)\n\n 第一个system就是占用的程序\n\n- 根据名称结束该进程\n\n ```shell\n taskkill /f /t /im java.exe\n ```\n\n 结束java.exe\n\n 结束成功会提示:\n 成功: 已终止 。。。\n\n 或者通过pid终止:\n\n ```shell\n taskkill /f /pid 1828\n ```\n\n \n\n ![根据名称 结束该程序](https://stepimagewm.how2j.cn/1565.png)\n\n\n\n\n\n> 也可以把配置文件 `server.xml` 的默认端口号更改\n>\n\n","source":"_posts/javaEE/Tomcat基础.md","raw":"---\ntitle: Tomcat基础\ntags:\n - Tomcat\ncategories:\n - - java\n - javaEE\nabbrlink: 530cfffd\ndate: 2021-06-11 13:31:26\ndescription: Web基础知识、服务器、Tomcat介绍、安装与使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210901182628_tomcat.png\n---\n\n# Tomcat了解\n\n\n\n## web概念\n\n1. 软件架构\n - C/S:客户端/服务器端(QQ、360。。。)\n - B/S:浏览器/服务器端(京东、淘宝、网易。。。)\n2. 资源分类\n - 静态资源(所有用户访问得到的结果一样)\n - 动态资源(不同用户访问的结果不同)\n3. 网络通信三要素\n - 协议\n - tcp:安全协议、三次握手、速度较慢\n - udp:不安全、速度快\n - ip:电子设备在网络中唯一标识\n - 端口号\n\n\n\n## 常见web服务器软件\n\n1. webLogic:大型JavaEE服务器,属于Oracle公司,收费\n2. webSphere:IBM公司\n3. JBOSS:JBOSS公司\n4. Tomcat:Apache组织\n\n\n\n## Tomcat介绍\n\n[Tomcat下载](https://tomcat.apache.org/download-80.cgi)\n\n\n\nTomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用[服务器](https://baike.baidu.com/item/服务器),在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应[HTML](https://baike.baidu.com/item/HTML)页面的访问请求。\n\n实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。\n\n\n\n### 目录结构\n\n![image-20210903200423036](https://gitee.com/ajream/images/raw/master/img/20210903200427_image-20210903200423036.png)\n\n\n\n重点,服务器配置文件 `/conf/server.xml`\n\n配置访问端口号,默认是 `8080`,如果被占用了可以改为其它端口\n\n```xml\n\n```\n\n配置服务器地址\n\nlocalhost: 实际是指向域名 `127.0.0.1`\n\n```xml\n\n```\n\n{%note primary flat%}\n\n面试题:访问一个网站的流程?\n\n大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)\n\n1. 浏览器输入域名,回车\n2. 浏览器查找本地 `C:\\Windows\\System32\\drivers\\etc\\host`文件,进行域名解析\n3. 如果没有找到,则去DNS服务器查找\n\n{%endnote%}\n\n\n\n## Tomcat使用\n\n\n\n### 启动tomcat\n\n运行bin目录下的批处理文件 `startup.bat`(确认当前是jdk1.8或以上,用`java -version`查看)\n\n\n\n### 部署网页\n\n例如部署一个test.html文件\n\n1. 把test.html文件复制到**D:\\tomcat\\webapps\\ROOT** 目录下\n2. 启动tomcat\n3. 访问 http://127.0.0.1:8080/test.html\n\n\n\n### 改端口号\n\n8080是tomcat默认端口号,而平时上网默认使用80端口\n\ntomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。\n\n修改端口号步骤:\n\n1. 打开server.xml文件\n2. `ctrl+f`查询8080,将下面这句代码的`8080`修改为其他端口号,如80,8081,8082\n\n```xml\n\n```\n\n接着就可以直接通过 http://127.0.0.1/test.html访问网页了。\n\n80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。\n\n\n\n### 端口被占用\n\n端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可\n\n- 查看80端口被哪些程序占用\n\n ```shell\n netstat -ano|findstr \"80\"\n ```\n\n ![image-20210408072223224](https://gitee.com/ajream/images/raw/master/img/image-20210408072223224.png)\n\n 第一行就是占用端口“80”的进程(其他只是含有80这个字符串而已),最后一列对应的进程的pid\n\n- 根据pid查询对应应用程序\n\n ```sh\n tasklist|findstr \"4\"\n ```\n\n ![image-20210408072728546](https://gitee.com/ajream/images/raw/master/img/image-20210408072728546.png)\n\n 第一个system就是占用的程序\n\n- 根据名称结束该进程\n\n ```shell\n taskkill /f /t /im java.exe\n ```\n\n 结束java.exe\n\n 结束成功会提示:\n 成功: 已终止 。。。\n\n 或者通过pid终止:\n\n ```shell\n taskkill /f /pid 1828\n ```\n\n \n\n ![根据名称 结束该程序](https://stepimagewm.how2j.cn/1565.png)\n\n\n\n\n\n> 也可以把配置文件 `server.xml` 的默认端口号更改\n>\n\n","slug":"javaEE/Tomcat基础","published":1,"updated":"2021-09-03T12:21:26.646Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ql005makve7a1dc654","content":"

Tomcat了解

web概念

    \n
  1. 软件架构
      \n
    • C/S:客户端/服务器端(QQ、360。。。)
    • \n
    • B/S:浏览器/服务器端(京东、淘宝、网易。。。)
    • \n
    \n
  2. \n
  3. 资源分类
      \n
    • 静态资源(所有用户访问得到的结果一样)
    • \n
    • 动态资源(不同用户访问的结果不同)
    • \n
    \n
  4. \n
  5. 网络通信三要素
      \n
    • 协议
        \n
      • tcp:安全协议、三次握手、速度较慢
      • \n
      • udp:不安全、速度快
      • \n
      \n
    • \n
    • ip:电子设备在网络中唯一标识
    • \n
    • 端口号
    • \n
    \n
  6. \n
\n

常见web服务器软件

    \n
  1. webLogic:大型JavaEE服务器,属于Oracle公司,收费
  2. \n
  3. webSphere:IBM公司
  4. \n
  5. JBOSS:JBOSS公司
  6. \n
  7. Tomcat:Apache组织
  8. \n
\n

Tomcat介绍

Tomcat下载

\n

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML页面的访问请求。

\n

实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。

\n

目录结构

\"image-20210903200423036\"

\n

重点,服务器配置文件 /conf/server.xml

\n

配置访问端口号,默认是 8080,如果被占用了可以改为其它端口

\n
1
2
3
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
\n

配置服务器地址

\n

localhost: 实际是指向域名 127.0.0.1

\n
1
2
<Host name="localhost"  appBase="webapps"
unpackWARs="true" autoDeploy="true">
\n

面试题:访问一个网站的流程?

\n

大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)

\n
    \n
  1. 浏览器输入域名,回车
  2. \n
  3. 浏览器查找本地 C:\\Windows\\System32\\drivers\\etc\\host文件,进行域名解析
  4. \n
  5. 如果没有找到,则去DNS服务器查找
  6. \n
\n
\n

Tomcat使用

启动tomcat

运行bin目录下的批处理文件 startup.bat(确认当前是jdk1.8或以上,用java -version查看)

\n

部署网页

例如部署一个test.html文件

\n
    \n
  1. 把test.html文件复制到D:\\tomcat\\webapps\\ROOT 目录下
  2. \n
  3. 启动tomcat
  4. \n
  5. 访问 http://127.0.0.1:8080/test.html
  6. \n
\n

改端口号

8080是tomcat默认端口号,而平时上网默认使用80端口

\n

tomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。

\n

修改端口号步骤:

\n
    \n
  1. 打开server.xml文件
  2. \n
  3. ctrl+f查询8080,将下面这句代码的8080修改为其他端口号,如80,8081,8082
  4. \n
\n
1
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8543" />
\n

接着就可以直接通过 http://127.0.0.1/test.html访问网页了。

\n

80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。

\n

端口被占用

端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可

\n
    \n
  • 查看80端口被哪些程序占用

    \n
    1
    netstat -ano|findstr "80"
    \n

    \"image-20210408072223224\"

    \n

    第一行就是占用端口“80”的进程(其他只是含有80这个字符串而已),最后一列对应的进程的pid

    \n
  • \n
  • 根据pid查询对应应用程序

    \n
    1
    tasklist|findstr "4"
    \n

    \"image-20210408072728546\"

    \n

    第一个system就是占用的程序

    \n
  • \n
  • 根据名称结束该进程

    \n
    1
    taskkill /f /t /im java.exe
    \n

    结束java.exe

    \n

    结束成功会提示:
    成功: 已终止 。。。

    \n

    或者通过pid终止:

    \n
    1
    taskkill /f /pid 1828
    \n
  • \n
\n

\"根据名称

\n
\n

也可以把配置文件 server.xml 的默认端口号更改

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Tomcat了解

web概念

    \n
  1. 软件架构
      \n
    • C/S:客户端/服务器端(QQ、360。。。)
    • \n
    • B/S:浏览器/服务器端(京东、淘宝、网易。。。)
    • \n
    \n
  2. \n
  3. 资源分类
      \n
    • 静态资源(所有用户访问得到的结果一样)
    • \n
    • 动态资源(不同用户访问的结果不同)
    • \n
    \n
  4. \n
  5. 网络通信三要素
      \n
    • 协议
        \n
      • tcp:安全协议、三次握手、速度较慢
      • \n
      • udp:不安全、速度快
      • \n
      \n
    • \n
    • ip:电子设备在网络中唯一标识
    • \n
    • 端口号
    • \n
    \n
  6. \n
\n

常见web服务器软件

    \n
  1. webLogic:大型JavaEE服务器,属于Oracle公司,收费
  2. \n
  3. webSphere:IBM公司
  4. \n
  5. JBOSS:JBOSS公司
  6. \n
  7. Tomcat:Apache组织
  8. \n
\n

Tomcat介绍

Tomcat下载

\n

Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML页面的访问请求。

\n

实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。

\n

目录结构

\"image-20210903200423036\"

\n

重点,服务器配置文件 /conf/server.xml

\n

配置访问端口号,默认是 8080,如果被占用了可以改为其它端口

\n
1
2
3
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
\n

配置服务器地址

\n

localhost: 实际是指向域名 127.0.0.1

\n
1
2
<Host name="localhost"  appBase="webapps"
unpackWARs="true" autoDeploy="true">
\n

面试题:访问一个网站的流程?

\n

大概是下面这样:(实际会更加麻烦,下面只是简化后的样子)

\n
    \n
  1. 浏览器输入域名,回车
  2. \n
  3. 浏览器查找本地 C:\\Windows\\System32\\drivers\\etc\\host文件,进行域名解析
  4. \n
  5. 如果没有找到,则去DNS服务器查找
  6. \n
\n
\n

Tomcat使用

启动tomcat

运行bin目录下的批处理文件 startup.bat(确认当前是jdk1.8或以上,用java -version查看)

\n

部署网页

例如部署一个test.html文件

\n
    \n
  1. 把test.html文件复制到D:\\tomcat\\webapps\\ROOT 目录下
  2. \n
  3. 启动tomcat
  4. \n
  5. 访问 http://127.0.0.1:8080/test.html
  6. \n
\n

改端口号

8080是tomcat默认端口号,而平时上网默认使用80端口

\n

tomcat的端口配置相关信息在 server.xml中,server.xml 记录了非常多的tomcat配置信息,其中就包括端口。

\n

修改端口号步骤:

\n
    \n
  1. 打开server.xml文件
  2. \n
  3. ctrl+f查询8080,将下面这句代码的8080修改为其他端口号,如80,8081,8082
  4. \n
\n
1
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8543" />
\n

接着就可以直接通过 http://127.0.0.1/test.html访问网页了。

\n

80端口就是web服务默认的端口号,所以就不需要显式写这个端口号了。

\n

端口被占用

端口被占用就无法启动tomcat,所以要找到端口被占用的程序是哪一个,然后再关闭对应的程序即可

\n
    \n
  • 查看80端口被哪些程序占用

    \n
    1
    netstat -ano|findstr "80"
    \n

    \"image-20210408072223224\"

    \n

    第一行就是占用端口“80”的进程(其他只是含有80这个字符串而已),最后一列对应的进程的pid

    \n
  • \n
  • 根据pid查询对应应用程序

    \n
    1
    tasklist|findstr "4"
    \n

    \"image-20210408072728546\"

    \n

    第一个system就是占用的程序

    \n
  • \n
  • 根据名称结束该进程

    \n
    1
    taskkill /f /t /im java.exe
    \n

    结束java.exe

    \n

    结束成功会提示:
    成功: 已终止 。。。

    \n

    或者通过pid终止:

    \n
    1
    taskkill /f /pid 1828
    \n
  • \n
\n

\"根据名称

\n
\n

也可以把配置文件 server.xml 的默认端口号更改

\n
\n"},{"title":"10-装箱与拆箱","abbrlink":"3aac01ca","date":"2021-02-13T11:33:27.000Z","description":"java的基本数据类型实际上都有一个封装类,这样就可以更方便的操作这些基本数据类型,封装类与基本数据类型的转换即为装箱、拆箱","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 装箱与拆箱\n\n## 封装类\n\n> 所有的**基本类型**,都有对应的**类类型**\n> 比如int对应的类是Integer\n> 这种类就叫做封装类\n\n```java\nint i = 5;\n \n//把一个基本类型的变量,转换为Integer对象\nInteger it = new Integer(i);\n\n//把一个Integer对象,转换为一个基本类型的int\nint i2 = it.intValue();\n```\n\n## 10.2 Number类\n\n> 数字封装类有\n>\n> - Byte Short Integer Long\n> - Float Double\n>\n> 这些类都是抽象类`Number`的子类\n\n## 基本类型<->封装类型\n\n- 基本类型转换成封装类型\n\n```java\nint i = 5;\n \nInteger it = new Integer(i); //i转换成封装类型it\n \n```\n\n- 封装类型转换成基本类型\n\n```java\nint i = 5;\n\nInteger it = new Integer(i); //i转换成封装类型it\n\nint i2 = it.intValue(); //封装类型转it换成基本类型i2,i2与i是一样的\n```\n\n\n\n## 自动装箱与拆箱\n\n- 自动装箱:通过`=`符号自动把 基本类型 —> 类类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n ```\n\n- 自动拆箱:与自动装箱相反,通过`=`符号自动把 类类型 —> 基本类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n \n int i1 = it;\t\t//自动拆箱\n ```\n\n\n\n## int的最大值/最小值\n\nint的最大值可以通过其对应的封装类`Integer.MAX_VALUE`获取;\n\n同理,最小值通过 `Integer.MIN_VALUE` 获取;\n\n\n\n","source":"_posts/javaSE/10-装箱与拆箱.md","raw":"---\ntitle: 10-装箱与拆箱\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 3aac01ca\ndate: 2021-02-13 19:33:27\ndescription: java的基本数据类型实际上都有一个封装类,这样就可以更方便的操作这些基本数据类型,封装类与基本数据类型的转换即为装箱、拆箱\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 装箱与拆箱\n\n## 封装类\n\n> 所有的**基本类型**,都有对应的**类类型**\n> 比如int对应的类是Integer\n> 这种类就叫做封装类\n\n```java\nint i = 5;\n \n//把一个基本类型的变量,转换为Integer对象\nInteger it = new Integer(i);\n\n//把一个Integer对象,转换为一个基本类型的int\nint i2 = it.intValue();\n```\n\n## 10.2 Number类\n\n> 数字封装类有\n>\n> - Byte Short Integer Long\n> - Float Double\n>\n> 这些类都是抽象类`Number`的子类\n\n## 基本类型<->封装类型\n\n- 基本类型转换成封装类型\n\n```java\nint i = 5;\n \nInteger it = new Integer(i); //i转换成封装类型it\n \n```\n\n- 封装类型转换成基本类型\n\n```java\nint i = 5;\n\nInteger it = new Integer(i); //i转换成封装类型it\n\nint i2 = it.intValue(); //封装类型转it换成基本类型i2,i2与i是一样的\n```\n\n\n\n## 自动装箱与拆箱\n\n- 自动装箱:通过`=`符号自动把 基本类型 —> 类类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n ```\n\n- 自动拆箱:与自动装箱相反,通过`=`符号自动把 类类型 —> 基本类型\n\n ```java\n int i = 5;\n Integer it = i; //自动装箱\n \n int i1 = it;\t\t//自动拆箱\n ```\n\n\n\n## int的最大值/最小值\n\nint的最大值可以通过其对应的封装类`Integer.MAX_VALUE`获取;\n\n同理,最小值通过 `Integer.MIN_VALUE` 获取;\n\n\n\n","slug":"javaSE/10-装箱与拆箱","published":1,"updated":"2021-08-29T15:31:20.436Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qn005qakve26rcd19s","content":"

装箱与拆箱

封装类

\n

所有的基本类型,都有对应的类类型
比如int对应的类是Integer
这种类就叫做封装类

\n
\n
1
2
3
4
5
6
7
int i = 5;

//把一个基本类型的变量,转换为Integer对象
Integer it = new Integer(i);

//把一个Integer对象,转换为一个基本类型的int
int i2 = it.intValue();
\n

10.2 Number类

\n

数字封装类有

\n
    \n
  • Byte Short Integer Long
  • \n
  • Float Double
  • \n
\n

这些类都是抽象类Number的子类

\n
\n

基本类型<->封装类型

    \n
  • 基本类型转换成封装类型
  • \n
\n
1
2
3
4
int i = 5;

Integer it = new Integer(i); //i转换成封装类型it

\n
    \n
  • 封装类型转换成基本类型
  • \n
\n
1
2
3
4
5
int i = 5;

Integer it = new Integer(i); //i转换成封装类型it

int i2 = it.intValue(); //封装类型转it换成基本类型i2,i2与i是一样的
\n

自动装箱与拆箱

    \n
  • 自动装箱:通过=符号自动把 基本类型 —> 类类型

    \n
    1
    2
    int i = 5;
    Integer it = i; //自动装箱
    \n
  • \n
  • 自动拆箱:与自动装箱相反,通过=符号自动把 类类型 —> 基本类型

    \n
    1
    2
    3
    4
    int i = 5;
    Integer it = i; //自动装箱

    int i1 = it;\t\t//自动拆箱
    \n
  • \n
\n

int的最大值/最小值

int的最大值可以通过其对应的封装类Integer.MAX_VALUE获取;

\n

同理,最小值通过 Integer.MIN_VALUE 获取;

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

装箱与拆箱

封装类

\n

所有的基本类型,都有对应的类类型
比如int对应的类是Integer
这种类就叫做封装类

\n
\n
1
2
3
4
5
6
7
int i = 5;

//把一个基本类型的变量,转换为Integer对象
Integer it = new Integer(i);

//把一个Integer对象,转换为一个基本类型的int
int i2 = it.intValue();
\n

10.2 Number类

\n

数字封装类有

\n
    \n
  • Byte Short Integer Long
  • \n
  • Float Double
  • \n
\n

这些类都是抽象类Number的子类

\n
\n

基本类型<->封装类型

    \n
  • 基本类型转换成封装类型
  • \n
\n
1
2
3
4
int i = 5;

Integer it = new Integer(i); //i转换成封装类型it

\n
    \n
  • 封装类型转换成基本类型
  • \n
\n
1
2
3
4
5
int i = 5;

Integer it = new Integer(i); //i转换成封装类型it

int i2 = it.intValue(); //封装类型转it换成基本类型i2,i2与i是一样的
\n

自动装箱与拆箱

    \n
  • 自动装箱:通过=符号自动把 基本类型 —> 类类型

    \n
    1
    2
    int i = 5;
    Integer it = i; //自动装箱
    \n
  • \n
  • 自动拆箱:与自动装箱相反,通过=符号自动把 类类型 —> 基本类型

    \n
    1
    2
    3
    4
    int i = 5;
    Integer it = i; //自动装箱

    int i1 = it;\t\t//自动拆箱
    \n
  • \n
\n

int的最大值/最小值

int的最大值可以通过其对应的封装类Integer.MAX_VALUE获取;

\n

同理,最小值通过 Integer.MIN_VALUE 获取;

\n"},{"title":"15-异常处理","abbrlink":"f5c95c","date":"2021-02-15T02:46:30.000Z","description":"java中的异常处理","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 异常处理\n\n## 异常定义\n\n导致程序的正常流程被中断的事件,叫做异常\n\n异常处理常见手段:try catch finally throws\n\n\n\n例如:文件不存在异常\n\n```java\npackage ExceptionProcess;\n\nimport java.io.File;\nimport java.io.FileInputStream;\n\npublic class Test {\n\n public static void main(String[] args){\n File f = new File(\"D:/LOL.exe\");\n new FileInputStream(f);\n }\n \n}\n\n\n/*输出:\nException in thread \"main\" java.lang.Error: Unresolved compilation problem:\n Unhandled exception type FileNotFoundException\n\n at ExceptionProcess.Test.main(Test.java:10)\n*/\n```\n\n\n\n## 异常处理\n\n异常处理常见手段: try catch finally throws\n\n### try catch\n\n1. 将可能抛出FileNotFoundException **文件不存在异常**的代码放在try里\n\n2. 如果文件存在,就会顺序往下执行,并且不执行catch块中的代码\n\n3. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中\n\n4. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n\n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 使用异常的父类进行catch\n\n`FileNotFoundException`是`Exception`的子类,使用Exception也可以catch住 FileNotFoundException\n\n```java\nFile f= new File(\"d:/LOL.exe\");\n\ntry{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n}\n\ncatch(Exception e){ \n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法一\n\n有的时候一段代码会抛出多种异常,比如\n\n```java\nnew FileInputStream(f);\nDate d = sdf.parse(\"2016-06-03\");\n```\n\n这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException\n解决办法之一是**分别进行catch**\n\n```java\ncatch (FileNotFoundException e) {\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n \n} catch (ParseException e) {\n System.out.println(\"日期格式解析错误\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法2\n\n另一个种办法是把多个异常,放在一个catch里统一捕捉\n\n```java\ncatch (FileNotFoundException | ParseException e) {\n \n}\n```\n\n这种方式从 JDK7开始支持,好处是捕捉的代码**更紧凑**,不足之处是,一旦发生异常,**不能确定到底是哪种异常**,需要通过`instanceof` 进行判断具体的异常类型\n\n```java\nif (e instanceof FileNotFoundException)\n\tSystem.out.println(\"d:/LOL.exe不存在\");\nif (e instanceof ParseException)\n\tSystem.out.println(\"日期格式解析错误\");\n```\n\n\n\n例如:\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n Date d = sdf.parse(\"2016-06-03\");\n \n } catch (FileNotFoundException | ParseException e) {\n if (e instanceof FileNotFoundException)\n System.out.println(\"d:/LOL.exe不存在\");\n if (e instanceof ParseException)\n System.out.println(\"日期格式解析错误\");\n \n e.printStackTrace();\n }\n \n }\n}\n```\n\n### finally\n\n无论是否出现异常,finally中的代码都会被执行\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f= new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n finally{\n System.out.println(\"无论文件是否存在, 都会执行的代码\");\n }\n }\n}\n```\n\n\n\n### throws\n\n> 考虑如下情况:\n>\n> 1. 主方法调用method1\n> 2. method1调用method2\n> 3. method2中打开文件\n> 4. method2中需要进行异常处理\n> 但是method2**不打算处理**,而是把这个异常通过**throws**抛出去\n> 5. 那么method1就会**接到该异常**。 处理办法也是两种,要么是try catch处理掉,要么也是**抛出去**。\n> 6. method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n method1();\n \n }\n \n private static void method1() {\n try {\n method2();\n } catch (FileNotFoundException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n \n private static void method2() throws FileNotFoundException {\n \n File f = new File(\"d:/LOL.exe\");\n \n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n \n }\n}\n```\n\n\n\n### throws与throw区别\n\nthrows与throw这两个关键字接近,不过意义不一样,有如下区别:\n\n1. throws 出现在方法**声明**上,而throw通常都出现在**方法体内**。\n\n2. - throws 表示出现异常的一种可能性,并不一定会发生这些异常;\n\n - throw则是抛出了异常,执行throw则一定抛出了某个异常对象\n\n## 异常分类\n\n- 可查异常(CheckedException)\n- 非可查异常\n - 运行时异常(RunTimeException)\n - 错误(Error)![三种分类](https://stepimagewm.how2j.cn/2412.png)\n\n\n\n### 可查异常\n\n可查异常即**必须进行处理的异常**,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException\n如果不处理,编译器,就不让你通过\n\n\n\n### 运行时异常\n\n运行时异常RuntimeException指: **不是必须进行try catch的异常**\n\n**常见运行时异常:**\n\n- 除数不能为0异常:ArithmeticException\n- 下标越界异常:ArrayIndexOutOfBoundsException\n- 空指针异常:NullPointerException\n\n在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,**即便不进行try catch,也不会有编译错误**。\n\nJava之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常**太过于普遍**,如果都需要进行捕捉,代码的可读性就会变得很糟糕。\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n //任何除数不能为0:ArithmeticException\n int k = 5/0;\n \n //下标越界异常:ArrayIndexOutOfBoundsException\n int j[] = new int[5];\n j[10] = 10;\n \n //空指针异常:NullPointerException\n String str = null;\n str.length();\n }\n}\n```\n\n### 错误Error\n\n指的是**系统级别的异常**,通常是内存用光了\n\n在**默认设置下**,一般java程序启动的时候,最大可以使用16m的内存\n\n如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出`OutOfMemoryError`,与运行时异常一样,错误也是不要求强制捕捉的\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n StringBuffer sb =new StringBuffer();\n \n for (int i = 0; i < Integer.MAX_VALUE; i++) {\n sb.append('a');\n }\n \n }\n \n}\n```\n\n\n\n## Throwable\n\n### Throwable\n\nThrowable是类,Exception和Error都继承了该类\n所以在捕捉的时候,也可以使用Throwable进行捕捉\n如图: 异常分**Error**和**Exception**\nException里又分**运行时异常**和**可查异常**\n\n![Throwable](https://stepimagewm.how2j.cn/742.png)\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n new FileInputStream(f);\n //使用Throwable进行异常捕捉\n } catch (Throwable t) {\n // TODO Auto-generated catch block\n t.printStackTrace();\n }\n \n }\n}\n```\n\n## 自定义异常\n\n### 创建自定义异常\n\n一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException\n创建一个类EnemyHeroIsDeadException,并继承Exception\n提供两个构造方法\n\n1. 无参的构造方法\n2. 带参的构造方法,并调用父类的对应的构造方法\n\n```java\nclass EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n}\n```\n\n\n\n### 抛出自定义异常\n\n在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常\n\n1. 创建一个EnemyHeroIsDeadException实例\n\n2. 通过**throw** 抛出该异常\n\n3. 当前方法通过 **throws** 抛出该异常\n\n在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void attackHero(Hero h) throws EnemyHeroIsDeadException{\n if(h.hp == 0){\n throw new EnemyHeroIsDeadException(h.name + \" 已经挂了,不需要施放技能\" );\n }\n }\n \n public String toString(){\n return name;\n }\n \n class EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n garen.hp = 616;\n \n Hero teemo = new Hero();\n teemo.name = \"提莫\";\n teemo.hp = 0;\n \n try {\n garen.attackHero(teemo);\n \n } catch (EnemyHeroIsDeadException e) {\n // TODO Auto-generated catch block\n System.out.println(\"异常的具体原因:\"+e.getMessage());\n e.printStackTrace();\n }\n \n }\n}\n```\n\n输出:\n\n![抛出自定义异常](https://stepimagewm.how2j.cn/744.png)","source":"_posts/javaSE/15-异常处理.md","raw":"---\ntitle: 15-异常处理\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: f5c95c\ndate: 2021-02-15 10:46:30\ndescription: java中的异常处理\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 异常处理\n\n## 异常定义\n\n导致程序的正常流程被中断的事件,叫做异常\n\n异常处理常见手段:try catch finally throws\n\n\n\n例如:文件不存在异常\n\n```java\npackage ExceptionProcess;\n\nimport java.io.File;\nimport java.io.FileInputStream;\n\npublic class Test {\n\n public static void main(String[] args){\n File f = new File(\"D:/LOL.exe\");\n new FileInputStream(f);\n }\n \n}\n\n\n/*输出:\nException in thread \"main\" java.lang.Error: Unresolved compilation problem:\n Unhandled exception type FileNotFoundException\n\n at ExceptionProcess.Test.main(Test.java:10)\n*/\n```\n\n\n\n## 异常处理\n\n异常处理常见手段: try catch finally throws\n\n### try catch\n\n1. 将可能抛出FileNotFoundException **文件不存在异常**的代码放在try里\n\n2. 如果文件存在,就会顺序往下执行,并且不执行catch块中的代码\n\n3. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中\n\n4. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n\n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 使用异常的父类进行catch\n\n`FileNotFoundException`是`Exception`的子类,使用Exception也可以catch住 FileNotFoundException\n\n```java\nFile f= new File(\"d:/LOL.exe\");\n\ntry{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n}\n\ncatch(Exception e){ \n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法一\n\n有的时候一段代码会抛出多种异常,比如\n\n```java\nnew FileInputStream(f);\nDate d = sdf.parse(\"2016-06-03\");\n```\n\n这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException\n解决办法之一是**分别进行catch**\n\n```java\ncatch (FileNotFoundException e) {\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n \n} catch (ParseException e) {\n System.out.println(\"日期格式解析错误\");\n e.printStackTrace();\n}\n```\n\n\n\n### 多异常捕捉办法2\n\n另一个种办法是把多个异常,放在一个catch里统一捕捉\n\n```java\ncatch (FileNotFoundException | ParseException e) {\n \n}\n```\n\n这种方式从 JDK7开始支持,好处是捕捉的代码**更紧凑**,不足之处是,一旦发生异常,**不能确定到底是哪种异常**,需要通过`instanceof` 进行判断具体的异常类型\n\n```java\nif (e instanceof FileNotFoundException)\n\tSystem.out.println(\"d:/LOL.exe不存在\");\nif (e instanceof ParseException)\n\tSystem.out.println(\"日期格式解析错误\");\n```\n\n\n\n例如:\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd\");\n Date d = sdf.parse(\"2016-06-03\");\n \n } catch (FileNotFoundException | ParseException e) {\n if (e instanceof FileNotFoundException)\n System.out.println(\"d:/LOL.exe不存在\");\n if (e instanceof ParseException)\n System.out.println(\"日期格式解析错误\");\n \n e.printStackTrace();\n }\n \n }\n}\n```\n\n### finally\n\n无论是否出现异常,finally中的代码都会被执行\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f= new File(\"d:/LOL.exe\");\n \n try{\n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n }\n catch(FileNotFoundException e){\n System.out.println(\"d:/LOL.exe不存在\");\n e.printStackTrace();\n }\n finally{\n System.out.println(\"无论文件是否存在, 都会执行的代码\");\n }\n }\n}\n```\n\n\n\n### throws\n\n> 考虑如下情况:\n>\n> 1. 主方法调用method1\n> 2. method1调用method2\n> 3. method2中打开文件\n> 4. method2中需要进行异常处理\n> 但是method2**不打算处理**,而是把这个异常通过**throws**抛出去\n> 5. 那么method1就会**接到该异常**。 处理办法也是两种,要么是try catch处理掉,要么也是**抛出去**。\n> 6. method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\n \npublic class TestException {\n \n public static void main(String[] args) {\n method1();\n \n }\n \n private static void method1() {\n try {\n method2();\n } catch (FileNotFoundException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n \n private static void method2() throws FileNotFoundException {\n \n File f = new File(\"d:/LOL.exe\");\n \n System.out.println(\"试图打开 d:/LOL.exe\");\n new FileInputStream(f);\n System.out.println(\"成功打开\");\n \n }\n}\n```\n\n\n\n### throws与throw区别\n\nthrows与throw这两个关键字接近,不过意义不一样,有如下区别:\n\n1. throws 出现在方法**声明**上,而throw通常都出现在**方法体内**。\n\n2. - throws 表示出现异常的一种可能性,并不一定会发生这些异常;\n\n - throw则是抛出了异常,执行throw则一定抛出了某个异常对象\n\n## 异常分类\n\n- 可查异常(CheckedException)\n- 非可查异常\n - 运行时异常(RunTimeException)\n - 错误(Error)![三种分类](https://stepimagewm.how2j.cn/2412.png)\n\n\n\n### 可查异常\n\n可查异常即**必须进行处理的异常**,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException\n如果不处理,编译器,就不让你通过\n\n\n\n### 运行时异常\n\n运行时异常RuntimeException指: **不是必须进行try catch的异常**\n\n**常见运行时异常:**\n\n- 除数不能为0异常:ArithmeticException\n- 下标越界异常:ArrayIndexOutOfBoundsException\n- 空指针异常:NullPointerException\n\n在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,**即便不进行try catch,也不会有编译错误**。\n\nJava之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常**太过于普遍**,如果都需要进行捕捉,代码的可读性就会变得很糟糕。\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n //任何除数不能为0:ArithmeticException\n int k = 5/0;\n \n //下标越界异常:ArrayIndexOutOfBoundsException\n int j[] = new int[5];\n j[10] = 10;\n \n //空指针异常:NullPointerException\n String str = null;\n str.length();\n }\n}\n```\n\n### 错误Error\n\n指的是**系统级别的异常**,通常是内存用光了\n\n在**默认设置下**,一般java程序启动的时候,最大可以使用16m的内存\n\n如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出`OutOfMemoryError`,与运行时异常一样,错误也是不要求强制捕捉的\n\n```java\npackage exception;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n StringBuffer sb =new StringBuffer();\n \n for (int i = 0; i < Integer.MAX_VALUE; i++) {\n sb.append('a');\n }\n \n }\n \n}\n```\n\n\n\n## Throwable\n\n### Throwable\n\nThrowable是类,Exception和Error都继承了该类\n所以在捕捉的时候,也可以使用Throwable进行捕捉\n如图: 异常分**Error**和**Exception**\nException里又分**运行时异常**和**可查异常**\n\n![Throwable](https://stepimagewm.how2j.cn/742.png)\n\n```java\npackage exception;\n \nimport java.io.File;\nimport java.io.FileInputStream;\n \npublic class TestException {\n \n public static void main(String[] args) {\n \n File f = new File(\"d:/LOL.exe\");\n \n try {\n new FileInputStream(f);\n //使用Throwable进行异常捕捉\n } catch (Throwable t) {\n // TODO Auto-generated catch block\n t.printStackTrace();\n }\n \n }\n}\n```\n\n## 自定义异常\n\n### 创建自定义异常\n\n一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException\n创建一个类EnemyHeroIsDeadException,并继承Exception\n提供两个构造方法\n\n1. 无参的构造方法\n2. 带参的构造方法,并调用父类的对应的构造方法\n\n```java\nclass EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n}\n```\n\n\n\n### 抛出自定义异常\n\n在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常\n\n1. 创建一个EnemyHeroIsDeadException实例\n\n2. 通过**throw** 抛出该异常\n\n3. 当前方法通过 **throws** 抛出该异常\n\n在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void attackHero(Hero h) throws EnemyHeroIsDeadException{\n if(h.hp == 0){\n throw new EnemyHeroIsDeadException(h.name + \" 已经挂了,不需要施放技能\" );\n }\n }\n \n public String toString(){\n return name;\n }\n \n class EnemyHeroIsDeadException extends Exception{\n \n public EnemyHeroIsDeadException(){\n \n }\n public EnemyHeroIsDeadException(String msg){\n super(msg);\n }\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n garen.hp = 616;\n \n Hero teemo = new Hero();\n teemo.name = \"提莫\";\n teemo.hp = 0;\n \n try {\n garen.attackHero(teemo);\n \n } catch (EnemyHeroIsDeadException e) {\n // TODO Auto-generated catch block\n System.out.println(\"异常的具体原因:\"+e.getMessage());\n e.printStackTrace();\n }\n \n }\n}\n```\n\n输出:\n\n![抛出自定义异常](https://stepimagewm.how2j.cn/744.png)","slug":"javaSE/15-异常处理","published":1,"updated":"2021-08-29T15:31:38.885Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qn005takveawfj22pb","content":"

异常处理

异常定义

导致程序的正常流程被中断的事件,叫做异常

\n

异常处理常见手段:try catch finally throws

\n

例如:文件不存在异常

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package ExceptionProcess;

import java.io.File;
import java.io.FileInputStream;

public class Test {

public static void main(String[] args){
File f = new File("D:/LOL.exe");
new FileInputStream(f);
}

}


/*输出:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unhandled exception type FileNotFoundException

at ExceptionProcess.Test.main(Test.java:10)
*/
\n

异常处理

异常处理常见手段: try catch finally throws

\n

try catch

    \n
  1. 将可能抛出FileNotFoundException 文件不存在异常的代码放在try里

    \n
  2. \n
  3. 如果文件存在,就会顺序往下执行,并且不执行catch块中的代码

    \n
  4. \n
  5. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中

    \n
  6. \n
  7. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常

    \n
  8. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}
catch(FileNotFoundException e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}

}
}
\n

使用异常的父类进行catch

FileNotFoundExceptionException的子类,使用Exception也可以catch住 FileNotFoundException

\n
1
2
3
4
5
6
7
8
9
10
11
12
File f= new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}

catch(Exception e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}
\n

多异常捕捉办法一

有的时候一段代码会抛出多种异常,比如

\n
1
2
new FileInputStream(f);
Date d = sdf.parse("2016-06-03");
\n

这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException
解决办法之一是分别进行catch

\n
1
2
3
4
5
6
7
8
catch (FileNotFoundException e) {
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();

} catch (ParseException e) {
System.out.println("日期格式解析错误");
e.printStackTrace();
}
\n

多异常捕捉办法2

另一个种办法是把多个异常,放在一个catch里统一捕捉

\n
1
2
3
catch (FileNotFoundException | ParseException e) {

}
\n

这种方式从 JDK7开始支持,好处是捕捉的代码更紧凑,不足之处是,一旦发生异常,不能确定到底是哪种异常,需要通过instanceof 进行判断具体的异常类型

\n
1
2
3
4
if (e instanceof FileNotFoundException)
\tSystem.out.println("d:/LOL.exe不存在");
if (e instanceof ParseException)
\tSystem.out.println("日期格式解析错误");
\n

例如:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try {
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse("2016-06-03");

} catch (FileNotFoundException | ParseException e) {
if (e instanceof FileNotFoundException)
System.out.println("d:/LOL.exe不存在");
if (e instanceof ParseException)
System.out.println("日期格式解析错误");

e.printStackTrace();
}

}
}
\n

finally

无论是否出现异常,finally中的代码都会被执行

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {

File f= new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}
catch(FileNotFoundException e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}
finally{
System.out.println("无论文件是否存在, 都会执行的代码");
}
}
}
\n

throws

\n

考虑如下情况:

\n
    \n
  1. 主方法调用method1
  2. \n
  3. method1调用method2
  4. \n
  5. method2中打开文件
  6. \n
  7. method2中需要进行异常处理
    但是method2不打算处理,而是把这个异常通过throws抛出去
  8. \n
  9. 那么method1就会接到该异常。 处理办法也是两种,要么是try catch处理掉,要么也是抛出去
  10. \n
  11. method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了
  12. \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {
method1();

}

private static void method1() {
try {
method2();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

private static void method2() throws FileNotFoundException {

File f = new File("d:/LOL.exe");

System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");

}
}
\n

throws与throw区别

throws与throw这两个关键字接近,不过意义不一样,有如下区别:

\n
    \n
  1. throws 出现在方法声明上,而throw通常都出现在方法体内

    \n
  2. \n
    • \n
    • throws 表示出现异常的一种可能性,并不一定会发生这些异常;

      \n
    • \n
    • throw则是抛出了异常,执行throw则一定抛出了某个异常对象

      \n
    • \n
    \n
  3. \n
\n

异常分类

    \n
  • 可查异常(CheckedException)
  • \n
  • 非可查异常
      \n
    • 运行时异常(RunTimeException)
    • \n
    • 错误(Error)\"三种分类\"
    • \n
    \n
  • \n
\n

可查异常

可查异常即必须进行处理的异常,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException
如果不处理,编译器,就不让你通过

\n

运行时异常

运行时异常RuntimeException指: 不是必须进行try catch的异常

\n

常见运行时异常:

\n
    \n
  • 除数不能为0异常:ArithmeticException
  • \n
  • 下标越界异常:ArrayIndexOutOfBoundsException
  • \n
  • 空指针异常:NullPointerException
  • \n
\n

在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,即便不进行try catch,也不会有编译错误

\n

Java之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常太过于普遍,如果都需要进行捕捉,代码的可读性就会变得很糟糕。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package exception;

public class TestException {

public static void main(String[] args) {

//任何除数不能为0:ArithmeticException
int k = 5/0;

//下标越界异常:ArrayIndexOutOfBoundsException
int j[] = new int[5];
j[10] = 10;

//空指针异常:NullPointerException
String str = null;
str.length();
}
}
\n

错误Error

指的是系统级别的异常,通常是内存用光了

\n

默认设置下,一般java程序启动的时候,最大可以使用16m的内存

\n

如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出OutOfMemoryError,与运行时异常一样,错误也是不要求强制捕捉的

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package exception;

public class TestException {

public static void main(String[] args) {

StringBuffer sb =new StringBuffer();

for (int i = 0; i < Integer.MAX_VALUE; i++) {
sb.append('a');
}

}

}
\n

Throwable

Throwable

Throwable是类,Exception和Error都继承了该类
所以在捕捉的时候,也可以使用Throwable进行捕捉
如图: 异常分ErrorException
Exception里又分运行时异常可查异常

\n

\"Throwable\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package exception;

import java.io.File;
import java.io.FileInputStream;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try {
new FileInputStream(f);
//使用Throwable进行异常捕捉
} catch (Throwable t) {
// TODO Auto-generated catch block
t.printStackTrace();
}

}
}
\n

自定义异常

创建自定义异常

一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException
创建一个类EnemyHeroIsDeadException,并继承Exception
提供两个构造方法

\n
    \n
  1. 无参的构造方法
  2. \n
  3. 带参的构造方法,并调用父类的对应的构造方法
  4. \n
\n
1
2
3
4
5
6
7
8
9
class EnemyHeroIsDeadException extends Exception{

public EnemyHeroIsDeadException(){

}
public EnemyHeroIsDeadException(String msg){
super(msg);
}
}
\n

抛出自定义异常

在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常

\n
    \n
  1. 创建一个EnemyHeroIsDeadException实例

    \n
  2. \n
  3. 通过throw 抛出该异常

    \n
  4. \n
  5. 当前方法通过 throws 抛出该异常

    \n
  6. \n
\n

在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package charactor;

public class Hero {
public String name;
protected float hp;

public void attackHero(Hero h) throws EnemyHeroIsDeadException{
if(h.hp == 0){
throw new EnemyHeroIsDeadException(h.name + " 已经挂了,不需要施放技能" );
}
}

public String toString(){
return name;
}

class EnemyHeroIsDeadException extends Exception{

public EnemyHeroIsDeadException(){

}
public EnemyHeroIsDeadException(String msg){
super(msg);
}
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";
garen.hp = 616;

Hero teemo = new Hero();
teemo.name = "提莫";
teemo.hp = 0;

try {
garen.attackHero(teemo);

} catch (EnemyHeroIsDeadException e) {
// TODO Auto-generated catch block
System.out.println("异常的具体原因:"+e.getMessage());
e.printStackTrace();
}

}
}
\n

输出:

\n

\"抛出自定义异常\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

异常处理

异常定义

导致程序的正常流程被中断的事件,叫做异常

\n

异常处理常见手段:try catch finally throws

\n

例如:文件不存在异常

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package ExceptionProcess;

import java.io.File;
import java.io.FileInputStream;

public class Test {

public static void main(String[] args){
File f = new File("D:/LOL.exe");
new FileInputStream(f);
}

}


/*输出:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Unhandled exception type FileNotFoundException

at ExceptionProcess.Test.main(Test.java:10)
*/
\n

异常处理

异常处理常见手段: try catch finally throws

\n

try catch

    \n
  1. 将可能抛出FileNotFoundException 文件不存在异常的代码放在try里

    \n
  2. \n
  3. 如果文件存在,就会顺序往下执行,并且不执行catch块中的代码

    \n
  4. \n
  5. 如果文件不存在,try 里的代码会立即终止,程序流程会运行到对应的catch块中

    \n
  6. \n
  7. e.printStackTrace(); 会打印出方法的调用痕迹,如此例,会打印出异常开始于TestException的第16行,这样就便于定位和分析到底哪里出了异常

    \n
  8. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}
catch(FileNotFoundException e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}

}
}
\n

使用异常的父类进行catch

FileNotFoundExceptionException的子类,使用Exception也可以catch住 FileNotFoundException

\n
1
2
3
4
5
6
7
8
9
10
11
12
File f= new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}

catch(Exception e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}
\n

多异常捕捉办法一

有的时候一段代码会抛出多种异常,比如

\n
1
2
new FileInputStream(f);
Date d = sdf.parse("2016-06-03");
\n

这段代码,会抛出 文件不存在异常 FileNotFoundException 和解析异常ParseException
解决办法之一是分别进行catch

\n
1
2
3
4
5
6
7
8
catch (FileNotFoundException e) {
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();

} catch (ParseException e) {
System.out.println("日期格式解析错误");
e.printStackTrace();
}
\n

多异常捕捉办法2

另一个种办法是把多个异常,放在一个catch里统一捕捉

\n
1
2
3
catch (FileNotFoundException | ParseException e) {

}
\n

这种方式从 JDK7开始支持,好处是捕捉的代码更紧凑,不足之处是,一旦发生异常,不能确定到底是哪种异常,需要通过instanceof 进行判断具体的异常类型

\n
1
2
3
4
if (e instanceof FileNotFoundException)
\tSystem.out.println("d:/LOL.exe不存在");
if (e instanceof ParseException)
\tSystem.out.println("日期格式解析错误");
\n

例如:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try {
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse("2016-06-03");

} catch (FileNotFoundException | ParseException e) {
if (e instanceof FileNotFoundException)
System.out.println("d:/LOL.exe不存在");
if (e instanceof ParseException)
System.out.println("日期格式解析错误");

e.printStackTrace();
}

}
}
\n

finally

无论是否出现异常,finally中的代码都会被执行

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {

File f= new File("d:/LOL.exe");

try{
System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");
}
catch(FileNotFoundException e){
System.out.println("d:/LOL.exe不存在");
e.printStackTrace();
}
finally{
System.out.println("无论文件是否存在, 都会执行的代码");
}
}
}
\n

throws

\n

考虑如下情况:

\n
    \n
  1. 主方法调用method1
  2. \n
  3. method1调用method2
  4. \n
  5. method2中打开文件
  6. \n
  7. method2中需要进行异常处理
    但是method2不打算处理,而是把这个异常通过throws抛出去
  8. \n
  9. 那么method1就会接到该异常。 处理办法也是两种,要么是try catch处理掉,要么也是抛出去
  10. \n
  11. method1选择本地try catch住 一旦try catch住了,就相当于把这个异常消化掉了,主方法在调用method1的时候,就不需要进行异常处理了
  12. \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package exception;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class TestException {

public static void main(String[] args) {
method1();

}

private static void method1() {
try {
method2();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

private static void method2() throws FileNotFoundException {

File f = new File("d:/LOL.exe");

System.out.println("试图打开 d:/LOL.exe");
new FileInputStream(f);
System.out.println("成功打开");

}
}
\n

throws与throw区别

throws与throw这两个关键字接近,不过意义不一样,有如下区别:

\n
    \n
  1. throws 出现在方法声明上,而throw通常都出现在方法体内

    \n
  2. \n
    • \n
    • throws 表示出现异常的一种可能性,并不一定会发生这些异常;

      \n
    • \n
    • throw则是抛出了异常,执行throw则一定抛出了某个异常对象

      \n
    • \n
    \n
  3. \n
\n

异常分类

    \n
  • 可查异常(CheckedException)
  • \n
  • 非可查异常
      \n
    • 运行时异常(RunTimeException)
    • \n
    • 错误(Error)\"三种分类\"
    • \n
    \n
  • \n
\n

可查异常

可查异常即必须进行处理的异常,要么try catch住,要么往外抛,谁调用,谁处理,比如 FileNotFoundException
如果不处理,编译器,就不让你通过

\n

运行时异常

运行时异常RuntimeException指: 不是必须进行try catch的异常

\n

常见运行时异常:

\n
    \n
  • 除数不能为0异常:ArithmeticException
  • \n
  • 下标越界异常:ArrayIndexOutOfBoundsException
  • \n
  • 空指针异常:NullPointerException
  • \n
\n

在编写代码的时候,依然可以使用try catch throws进行处理,与可查异常不同之处在于,即便不进行try catch,也不会有编译错误

\n

Java之所以会设计运行时异常的原因之一,是因为下标越界,空指针这些运行时异常太过于普遍,如果都需要进行捕捉,代码的可读性就会变得很糟糕。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package exception;

public class TestException {

public static void main(String[] args) {

//任何除数不能为0:ArithmeticException
int k = 5/0;

//下标越界异常:ArrayIndexOutOfBoundsException
int j[] = new int[5];
j[10] = 10;

//空指针异常:NullPointerException
String str = null;
str.length();
}
}
\n

错误Error

指的是系统级别的异常,通常是内存用光了

\n

默认设置下,一般java程序启动的时候,最大可以使用16m的内存

\n

如例不停的给StringBuffer追加字符,很快就把内存使用光了。抛出OutOfMemoryError,与运行时异常一样,错误也是不要求强制捕捉的

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package exception;

public class TestException {

public static void main(String[] args) {

StringBuffer sb =new StringBuffer();

for (int i = 0; i < Integer.MAX_VALUE; i++) {
sb.append('a');
}

}

}
\n

Throwable

Throwable

Throwable是类,Exception和Error都继承了该类
所以在捕捉的时候,也可以使用Throwable进行捕捉
如图: 异常分ErrorException
Exception里又分运行时异常可查异常

\n

\"Throwable\"

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package exception;

import java.io.File;
import java.io.FileInputStream;

public class TestException {

public static void main(String[] args) {

File f = new File("d:/LOL.exe");

try {
new FileInputStream(f);
//使用Throwable进行异常捕捉
} catch (Throwable t) {
// TODO Auto-generated catch block
t.printStackTrace();
}

}
}
\n

自定义异常

创建自定义异常

一个英雄攻击另一个英雄的时候,如果发现另一个英雄已经挂了,就会抛出EnemyHeroIsDeadException
创建一个类EnemyHeroIsDeadException,并继承Exception
提供两个构造方法

\n
    \n
  1. 无参的构造方法
  2. \n
  3. 带参的构造方法,并调用父类的对应的构造方法
  4. \n
\n
1
2
3
4
5
6
7
8
9
class EnemyHeroIsDeadException extends Exception{

public EnemyHeroIsDeadException(){

}
public EnemyHeroIsDeadException(String msg){
super(msg);
}
}
\n

抛出自定义异常

在Hero的attack方法中,当发现敌方英雄的血量为0的时候,抛出该异常

\n
    \n
  1. 创建一个EnemyHeroIsDeadException实例

    \n
  2. \n
  3. 通过throw 抛出该异常

    \n
  4. \n
  5. 当前方法通过 throws 抛出该异常

    \n
  6. \n
\n

在外部调用attack方法的时候,就需要进行捕捉,并且捕捉的时候,可以通过e.getMessage() 获取当时出错的具体原因

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package charactor;

public class Hero {
public String name;
protected float hp;

public void attackHero(Hero h) throws EnemyHeroIsDeadException{
if(h.hp == 0){
throw new EnemyHeroIsDeadException(h.name + " 已经挂了,不需要施放技能" );
}
}

public String toString(){
return name;
}

class EnemyHeroIsDeadException extends Exception{

public EnemyHeroIsDeadException(){

}
public EnemyHeroIsDeadException(String msg){
super(msg);
}
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";
garen.hp = 616;

Hero teemo = new Hero();
teemo.name = "提莫";
teemo.hp = 0;

try {
garen.attackHero(teemo);

} catch (EnemyHeroIsDeadException e) {
// TODO Auto-generated catch block
System.out.println("异常的具体原因:"+e.getMessage());
e.printStackTrace();
}

}
}
\n

输出:

\n

\"抛出自定义异常\"

\n"},{"title":"12-字符串操作","abbrlink":"5e64b19a","date":"2021-02-13T11:20:44.000Z","description":"java的字符串初始化、基本操作函数的使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 12 java字符串\n\nJava用`String` 和 `StringBuffer`类来处理字符串,在Java中,每个字符串都是一个对象。\n\n- String:主要用于内容不可改变的字符串对象,即字符串一旦创建就不能再改变了(只读)\n- StringBuffer:用于串内容可以改变的字符串对象(可读、可写)\n\n## 12.1字符串初始化\n\n以下用3种方法创建并初始化字符串对象\n\n```java\nString s1 = new String( \"hello\");\n\nString s2 = \"hello\";\n\nchar[] ch = {'h', 'e', 'l', 'l', 'o'};\nString s3 = new String(ch);\n\n```\n\n## 12.2 字符串连接\n\n字符串连接用 `+` 号\n\n```java\nString str1 = \"abc\";\nString str2 = \"123\";\n\nString str3 = str1 + str2; //字符串连接\n```\n\n\n\n## 12.3 字符串比较\n\n1. `==`只检查两个串**引用**是否相同,不比较串值;\n\n > 注意:\n >\n > 一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象\n > 所以在如下代码中:\n >\n > 第6行会创建了一个新的字符串\"the light\",\n >\n > 但是在第7行,编译器发现已经存在现成的\"the light\",那么就直接拿来使用,而没有进行重复创建\n\n ```java\n package character;\n \n public class TestString {\n \n public static void main(String[] args) {\n String str1 = \"the light\";\n String str3 = \"the light\";\n System.out.println( str1 == str3); //true\n }\n \n }\n ```\n\n \n\n2. `equals()`或`equalsIgnoreCase()`方法:比较串值是否相同\n\n `equalsIgnoreCase()`表示忽略大小写的影响\n\n \n\n3. `compareTo()`方法:串大小比较(字典序)\n\n\n\n```java\nstring str1 = \"abc\"; \nstring str2 = \"a\";\n\nstr2 += \"bc\"; \nif(str1 == str2){ }; //false\nif(str1.equals(str2)){ }; \t\t//true\n\n```\n\n\n\n## 12.4 字符串拆分\n\n`split(regex)`方法根据匹配确定的分隔符 `regex` 将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;\n\n```java\npublic class HelloWorld {\n public static void main(String[] args){\n\n String s = \"The-cat-sat-on-the-mat.\";\n \n String[] words = s.split(\"-\"); //从-处开始分割\n \n for(int i=0; i 注意:\n >\n > 一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象\n > 所以在如下代码中:\n >\n > 第6行会创建了一个新的字符串\"the light\",\n >\n > 但是在第7行,编译器发现已经存在现成的\"the light\",那么就直接拿来使用,而没有进行重复创建\n\n ```java\n package character;\n \n public class TestString {\n \n public static void main(String[] args) {\n String str1 = \"the light\";\n String str3 = \"the light\";\n System.out.println( str1 == str3); //true\n }\n \n }\n ```\n\n \n\n2. `equals()`或`equalsIgnoreCase()`方法:比较串值是否相同\n\n `equalsIgnoreCase()`表示忽略大小写的影响\n\n \n\n3. `compareTo()`方法:串大小比较(字典序)\n\n\n\n```java\nstring str1 = \"abc\"; \nstring str2 = \"a\";\n\nstr2 += \"bc\"; \nif(str1 == str2){ }; //false\nif(str1.equals(str2)){ }; \t\t//true\n\n```\n\n\n\n## 12.4 字符串拆分\n\n`split(regex)`方法根据匹配确定的分隔符 `regex` 将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;\n\n```java\npublic class HelloWorld {\n public static void main(String[] args){\n\n String s = \"The-cat-sat-on-the-mat.\";\n \n String[] words = s.split(\"-\"); //从-处开始分割\n \n for(int i=0; i12 java字符串

Java用StringStringBuffer类来处理字符串,在Java中,每个字符串都是一个对象。

\n
    \n
  • String:主要用于内容不可改变的字符串对象,即字符串一旦创建就不能再改变了(只读)
  • \n
  • StringBuffer:用于串内容可以改变的字符串对象(可读、可写)
  • \n
\n

12.1字符串初始化

以下用3种方法创建并初始化字符串对象

\n
1
2
3
4
5
6
7
String  s1 = new String( "hello");

String s2 = "hello";

char[] ch = {'h', 'e', 'l', 'l', 'o'};
String s3 = new String(ch);

\n

12.2 字符串连接

字符串连接用 +

\n
1
2
3
4
String  str1 = "abc";
String str2 = "123";

String str3 = str1 + str2; //字符串连接
\n

12.3 字符串比较

    \n
  1. ==只检查两个串引用是否相同,不比较串值;

    \n
    \n

    注意:

    \n

    一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象
    所以在如下代码中:

    \n

    第6行会创建了一个新的字符串”the light”,

    \n

    但是在第7行,编译器发现已经存在现成的”the light”,那么就直接拿来使用,而没有进行重复创建

    \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package character;

    public class TestString {

    public static void main(String[] args) {
    String str1 = "the light";
    String str3 = "the light";
    System.out.println( str1 == str3); //true
    }

    }
    \n
  2. \n
\n
    \n
  1. equals()equalsIgnoreCase()方法:比较串值是否相同

    \n

    equalsIgnoreCase()表示忽略大小写的影响

    \n
  2. \n
\n
    \n
  1. compareTo()方法:串大小比较(字典序)
  2. \n
\n
1
2
3
4
5
6
7
string  str1 = "abc";    
string str2 = "a";

str2 += "bc";
if(str1 == str2){ }; //false
if(str1.equals(str2)){ }; \t\t//true

\n

12.4 字符串拆分

split(regex)方法根据匹配确定的分隔符 regex 将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class HelloWorld {
public static void main(String[] args){

String s = "The-cat-sat-on-the-mat.";

String[] words = s.split("-"); //从-处开始分割

for(int i=0; i<words.length; i++)
{
System.out.println(words[i]);
}

}
}

/*输出:
The
cat
sat
on
the
mat.
*/
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloWorld {
public static void main(String[] args){

String s = "12+34+567";
String[] ss = s.split("[+]"); //注意中括号[]不能省略

int sum = 0;
for(int i=0;i<ss.length;i++)
{
sum += Integer.parseInt(ss[i]);
}
System.out.println(sum);

}
}

//输出:613
\n

12.5操作字符串常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int length();\t\t\t//返回字符串长度

char charAt(int index); //返回指定下标的字符

boolean equals(String s); //判断当前字符串和s串相等

int indexOf(String str); //返回str在串中首次出现位置

String concat(String str); //把str连接在当前串之后。

String substring(int begin ,int end); //截取子串[begin,end-1]

String toLowerCase(); //将串转换成小写并返回

String toUpperCase(); //将串转换成大写并返回

String trim(); //将串开始和结尾的空串去掉。

char[] toCharArray(); //返回对应的字符数组

String replace(char old, char new); //新字符替代旧字符

String replaceAll(String old, String new) // 用 `new`串替代字符串中【所有的】 `old`串

String replaceFirst(String old, String new) //只替代【第一个】出现的
\n

12.6 StringBuffer类

StringBuffer类创建的串其内容是可以修改的,其占用的空间能自动增长:

\n
1
String s = "a" + 4 + "b123" + false; \t//低效率
\n

提高效率被编译成下列等价代码:

\n
1
2
StringBuffer s = new StringBuffer(); 
s.append("a").append(4).append("b123").append(false).toString();
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

12 java字符串

Java用StringStringBuffer类来处理字符串,在Java中,每个字符串都是一个对象。

\n
    \n
  • String:主要用于内容不可改变的字符串对象,即字符串一旦创建就不能再改变了(只读)
  • \n
  • StringBuffer:用于串内容可以改变的字符串对象(可读、可写)
  • \n
\n

12.1字符串初始化

以下用3种方法创建并初始化字符串对象

\n
1
2
3
4
5
6
7
String  s1 = new String( "hello");

String s2 = "hello";

char[] ch = {'h', 'e', 'l', 'l', 'o'};
String s3 = new String(ch);

\n

12.2 字符串连接

字符串连接用 +

\n
1
2
3
4
String  str1 = "abc";
String str2 = "123";

String str3 = str1 + str2; //字符串连接
\n

12.3 字符串比较

    \n
  1. ==只检查两个串引用是否相同,不比较串值;

    \n
    \n

    注意:

    \n

    一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象
    所以在如下代码中:

    \n

    第6行会创建了一个新的字符串”the light”,

    \n

    但是在第7行,编译器发现已经存在现成的”the light”,那么就直接拿来使用,而没有进行重复创建

    \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    package character;

    public class TestString {

    public static void main(String[] args) {
    String str1 = "the light";
    String str3 = "the light";
    System.out.println( str1 == str3); //true
    }

    }
    \n
  2. \n
\n
    \n
  1. equals()equalsIgnoreCase()方法:比较串值是否相同

    \n

    equalsIgnoreCase()表示忽略大小写的影响

    \n
  2. \n
\n
    \n
  1. compareTo()方法:串大小比较(字典序)
  2. \n
\n
1
2
3
4
5
6
7
string  str1 = "abc";    
string str2 = "a";

str2 += "bc";
if(str1 == str2){ }; //false
if(str1.equals(str2)){ }; \t\t//true

\n

12.4 字符串拆分

split(regex)方法根据匹配确定的分隔符 regex 将串分解成子串,所有子串存储在字符串数组(每个成员是一个子串)中;

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class HelloWorld {
public static void main(String[] args){

String s = "The-cat-sat-on-the-mat.";

String[] words = s.split("-"); //从-处开始分割

for(int i=0; i<words.length; i++)
{
System.out.println(words[i]);
}

}
}

/*输出:
The
cat
sat
on
the
mat.
*/
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloWorld {
public static void main(String[] args){

String s = "12+34+567";
String[] ss = s.split("[+]"); //注意中括号[]不能省略

int sum = 0;
for(int i=0;i<ss.length;i++)
{
sum += Integer.parseInt(ss[i]);
}
System.out.println(sum);

}
}

//输出:613
\n

12.5操作字符串常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int length();\t\t\t//返回字符串长度

char charAt(int index); //返回指定下标的字符

boolean equals(String s); //判断当前字符串和s串相等

int indexOf(String str); //返回str在串中首次出现位置

String concat(String str); //把str连接在当前串之后。

String substring(int begin ,int end); //截取子串[begin,end-1]

String toLowerCase(); //将串转换成小写并返回

String toUpperCase(); //将串转换成大写并返回

String trim(); //将串开始和结尾的空串去掉。

char[] toCharArray(); //返回对应的字符数组

String replace(char old, char new); //新字符替代旧字符

String replaceAll(String old, String new) // 用 `new`串替代字符串中【所有的】 `old`串

String replaceFirst(String old, String new) //只替代【第一个】出现的
\n

12.6 StringBuffer类

StringBuffer类创建的串其内容是可以修改的,其占用的空间能自动增长:

\n
1
String s = "a" + 4 + "b123" + false; \t//低效率
\n

提高效率被编译成下列等价代码:

\n
1
2
StringBuffer s = new StringBuffer(); 
s.append("a").append(4).append("b123").append(false).toString();
\n"},{"title":"13-日期与日历","abbrlink":"b3cec184","date":"2021-02-14T02:15:21.000Z","description":"java的日期与日历数据类型,如何使用及与字符串的转换","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 日期\n\n在Java中使用日期,需导入 `java.util.Date` 类\n\n```java\nimport java.util.Date;\n```\n\n\n\n## 创建一个日期对象\n\n- `Date date = new Date(); ` \n\n```java\nDate now = new Date(); \n\n//不加参数表示当前时间\n//加了参数n表示从1970-01-01 8:00:00开始经历了n毫秒\n```\n\n- 日期对象转字符串对象:\n\n```java\nString s = now.toString()\n```\n\n\n\n## getTime()方法\n\n`getTime()`是Date()对象的一个方法,返回类型 `long`,用于获取从1970-01-01 8:00:00开始到现在经历的**毫秒**数\n\n另外,`Date().getTime()` 和 `System.currentTimeMillis()` 是一样的\n\n```java\npackage date;\n \n//\nimport java.util.Date;\n \npublic class TestDate {\n \n public static void main(String[] args) {\n Date now= new Date();\n \n //当前日期的毫秒数\n System.out.println(\"Date.getTime() \\t\\t\\t返回值: \"+now.getTime());\n \n //通过System.currentTimeMillis()获取当前日期的毫秒数\n System.out.println(\"System.currentTimeMillis() \\t返回值: \"+System.currentTimeMillis());\n \n }\n}\n\n/*输出:\n\nDate.getTime() 返回值: 1611591854927\nSystem.currentTimeMillis() 返回值: 1611591854927\n\n*/\n\n```\n\n\n\n## 格式化日期并转为字符串\n\n格式化日期:\n\n需要用到 `SimpleDateFormat` 类\n\n```java\nimport java.text.SimpleDateFormat;\n```\n\n1. 指定格式\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss SSS\" );\n \n //y 代表年\n //M 代表月\n //d 代表日\n //H 代表24进制的小时\n //h 代表12进制的小时\n //m 代表分钟\n //s 代表秒\n //S 代表毫秒\n \n // 以上7种可以只选其中一种或多种,根据自己需要来,如:\n SimpleDateFormat sdf1 = new SimpleDateFormat(\"yyyy-MM-dd\" );\n SimpleDateFormat sdf2 = new SimpleDateFormat(\"MM/dd\" );\n ```\n\n2. 利用sdf对象的 `format`方法对日期 `Date()`进行格式化,并返回格式化后的`字符串`\n\n ```java\n Date d = new Date();\n String str = sdf.format(d); //这样日期就按格式转为了字符串\n ```\n\n\n\n## 字符串转日期\n\n三个步骤 :\n\n1. 指定字符串格式:\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\" );\n \n //SimpleDateFormat sdf1 =new SimpleDateFormat(\"yyyy/MM/dd\" );\n ```\n\n2. 创建字符串,注意格式要与 sdf 一致\n\n ```java\n String str = \"2019-1-15 16:02:27\";\n \n //String str1 = \"2019-1-15\";\n ```\n\n3. 创建日期对象,用 sdf的 `parse(str)`方法来把字符串转为日期对象\n\n ```java\n Date d = sdf.parse(str);\n ```\n\n\n\n## 日历 Calendar\n\n使用前需要导入:\n\n```java\nimport java.util.Calendar;\n```\n\n\n\n1. 获取日历对象 `Calendar.getInstance();`\n\n ```java\n Calendar cl = Calendar.getInstance();\n ```\n\n2. 通过日历对象得到日期对象: `getTime()`\n\n ```java\n Date d = cl.getTime();\n ```\n\n3. 日期设置:把 `cl` 这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 `setTime()`\n\n ```java\n Date d2 = new Date(0);\n cl.setTime(d2);\n ```\n\n## 翻日历\n\n> add方法,在原日期上**增加(add)** 年/月/日\n> set方法,**直接设置(set)** 年/月/日\n\n1. 创建日历对象\n\n ```java\n Calendar c = Calendar.getInstance();\n ```\n\n2. 获取当前日期\n\n ```java\n Date now = c.getTime();\n ```\n\n3. 把日历改成下个月的今天\n\n ```java\n c.setTime(now); //表示把日历改为当前时间\n \n c.add(Calendar.MONTH, 1); \n //在当前时间now的基础上修改日历,这里修改了月份,下个月 = 本月 + 1\n ```\n\n4. 把日历改成去年的今天\n\n ```java\n c.setTime(now);\n c.add(Calendar.YEAR, -1); //负数表示以前的,去年 = 今年 - 1\n ```\n\n5. 把日历直接**设置**成上个月的第三天\n\n ```java\n c.setTime(now);\n c.add(Calendar.MONTH, -1); //先改月份\n c.set(Calendar.DATE, 3);\t\t//再改日数,注意用set\n \n //如果是今天后的第三天,则用:\n //c.add(Calendar.DATE, 3)\n ```\n\n ","source":"_posts/javaSE/13-日期.md","raw":"---\ntitle: 13-日期与日历\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: b3cec184\ndate: 2021-02-14 10:15:21\ndescription: java的日期与日历数据类型,如何使用及与字符串的转换\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 日期\n\n在Java中使用日期,需导入 `java.util.Date` 类\n\n```java\nimport java.util.Date;\n```\n\n\n\n## 创建一个日期对象\n\n- `Date date = new Date(); ` \n\n```java\nDate now = new Date(); \n\n//不加参数表示当前时间\n//加了参数n表示从1970-01-01 8:00:00开始经历了n毫秒\n```\n\n- 日期对象转字符串对象:\n\n```java\nString s = now.toString()\n```\n\n\n\n## getTime()方法\n\n`getTime()`是Date()对象的一个方法,返回类型 `long`,用于获取从1970-01-01 8:00:00开始到现在经历的**毫秒**数\n\n另外,`Date().getTime()` 和 `System.currentTimeMillis()` 是一样的\n\n```java\npackage date;\n \n//\nimport java.util.Date;\n \npublic class TestDate {\n \n public static void main(String[] args) {\n Date now= new Date();\n \n //当前日期的毫秒数\n System.out.println(\"Date.getTime() \\t\\t\\t返回值: \"+now.getTime());\n \n //通过System.currentTimeMillis()获取当前日期的毫秒数\n System.out.println(\"System.currentTimeMillis() \\t返回值: \"+System.currentTimeMillis());\n \n }\n}\n\n/*输出:\n\nDate.getTime() 返回值: 1611591854927\nSystem.currentTimeMillis() 返回值: 1611591854927\n\n*/\n\n```\n\n\n\n## 格式化日期并转为字符串\n\n格式化日期:\n\n需要用到 `SimpleDateFormat` 类\n\n```java\nimport java.text.SimpleDateFormat;\n```\n\n1. 指定格式\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss SSS\" );\n \n //y 代表年\n //M 代表月\n //d 代表日\n //H 代表24进制的小时\n //h 代表12进制的小时\n //m 代表分钟\n //s 代表秒\n //S 代表毫秒\n \n // 以上7种可以只选其中一种或多种,根据自己需要来,如:\n SimpleDateFormat sdf1 = new SimpleDateFormat(\"yyyy-MM-dd\" );\n SimpleDateFormat sdf2 = new SimpleDateFormat(\"MM/dd\" );\n ```\n\n2. 利用sdf对象的 `format`方法对日期 `Date()`进行格式化,并返回格式化后的`字符串`\n\n ```java\n Date d = new Date();\n String str = sdf.format(d); //这样日期就按格式转为了字符串\n ```\n\n\n\n## 字符串转日期\n\n三个步骤 :\n\n1. 指定字符串格式:\n\n ```java\n SimpleDateFormat sdf =new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\" );\n \n //SimpleDateFormat sdf1 =new SimpleDateFormat(\"yyyy/MM/dd\" );\n ```\n\n2. 创建字符串,注意格式要与 sdf 一致\n\n ```java\n String str = \"2019-1-15 16:02:27\";\n \n //String str1 = \"2019-1-15\";\n ```\n\n3. 创建日期对象,用 sdf的 `parse(str)`方法来把字符串转为日期对象\n\n ```java\n Date d = sdf.parse(str);\n ```\n\n\n\n## 日历 Calendar\n\n使用前需要导入:\n\n```java\nimport java.util.Calendar;\n```\n\n\n\n1. 获取日历对象 `Calendar.getInstance();`\n\n ```java\n Calendar cl = Calendar.getInstance();\n ```\n\n2. 通过日历对象得到日期对象: `getTime()`\n\n ```java\n Date d = cl.getTime();\n ```\n\n3. 日期设置:把 `cl` 这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 `setTime()`\n\n ```java\n Date d2 = new Date(0);\n cl.setTime(d2);\n ```\n\n## 翻日历\n\n> add方法,在原日期上**增加(add)** 年/月/日\n> set方法,**直接设置(set)** 年/月/日\n\n1. 创建日历对象\n\n ```java\n Calendar c = Calendar.getInstance();\n ```\n\n2. 获取当前日期\n\n ```java\n Date now = c.getTime();\n ```\n\n3. 把日历改成下个月的今天\n\n ```java\n c.setTime(now); //表示把日历改为当前时间\n \n c.add(Calendar.MONTH, 1); \n //在当前时间now的基础上修改日历,这里修改了月份,下个月 = 本月 + 1\n ```\n\n4. 把日历改成去年的今天\n\n ```java\n c.setTime(now);\n c.add(Calendar.YEAR, -1); //负数表示以前的,去年 = 今年 - 1\n ```\n\n5. 把日历直接**设置**成上个月的第三天\n\n ```java\n c.setTime(now);\n c.add(Calendar.MONTH, -1); //先改月份\n c.set(Calendar.DATE, 3);\t\t//再改日数,注意用set\n \n //如果是今天后的第三天,则用:\n //c.add(Calendar.DATE, 3)\n ```\n\n ","slug":"javaSE/13-日期","published":1,"updated":"2021-08-29T15:31:31.919Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qq005zakve2q775ly3","content":"

日期

在Java中使用日期,需导入 java.util.Date

\n
1
import java.util.Date;
\n

创建一个日期对象

    \n
  • Date date = new Date();
  • \n
\n
1
2
3
4
Date now = new Date(); 

//不加参数表示当前时间
//加了参数n表示从1970-01-01 8:00:00开始经历了n毫秒
\n
    \n
  • 日期对象转字符串对象:
  • \n
\n
1
String s = now.toString()
\n

getTime()方法

getTime()是Date()对象的一个方法,返回类型 long,用于获取从1970-01-01 8:00:00开始到现在经历的毫秒

\n

另外,Date().getTime()System.currentTimeMillis() 是一样的

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package date;

//
import java.util.Date;

public class TestDate {

public static void main(String[] args) {
Date now= new Date();

//当前日期的毫秒数
System.out.println("Date.getTime() \\t\\t\\t返回值: "+now.getTime());

//通过System.currentTimeMillis()获取当前日期的毫秒数
System.out.println("System.currentTimeMillis() \\t返回值: "+System.currentTimeMillis());

}
}

/*输出:

Date.getTime() 返回值: 1611591854927
System.currentTimeMillis() 返回值: 1611591854927

*/

\n

格式化日期并转为字符串

格式化日期:

\n

需要用到 SimpleDateFormat

\n
1
import java.text.SimpleDateFormat;
\n
    \n
  1. 指定格式

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );

    //y 代表年
    //M 代表月
    //d 代表日
    //H 代表24进制的小时
    //h 代表12进制的小时
    //m 代表分钟
    //s 代表秒
    //S 代表毫秒

    // 以上7种可以只选其中一种或多种,根据自己需要来,如:
    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd" );
    SimpleDateFormat sdf2 = new SimpleDateFormat("MM/dd" );
    \n
  2. \n
  3. 利用sdf对象的 format方法对日期 Date()进行格式化,并返回格式化后的字符串

    \n
    1
    2
    Date d = new Date();
    String str = sdf.format(d); //这样日期就按格式转为了字符串
    \n
  4. \n
\n

字符串转日期

三个步骤 :

\n
    \n
  1. 指定字符串格式:

    \n
    1
    2
    3
    SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" );

    //SimpleDateFormat sdf1 =new SimpleDateFormat("yyyy/MM/dd" );
    \n
  2. \n
  3. 创建字符串,注意格式要与 sdf 一致

    \n
    1
    2
    3
    String str = "2019-1-15 16:02:27";

    //String str1 = "2019-1-15";
    \n
  4. \n
  5. 创建日期对象,用 sdf的 parse(str)方法来把字符串转为日期对象

    \n
    1
    Date d = sdf.parse(str);
    \n
  6. \n
\n

日历 Calendar

使用前需要导入:

\n
1
import java.util.Calendar;
\n
    \n
  1. 获取日历对象 Calendar.getInstance();

    \n
    1
    Calendar cl = Calendar.getInstance();
    \n
  2. \n
  3. 通过日历对象得到日期对象: getTime()

    \n
    1
    Date d = cl.getTime();
    \n
  4. \n
  5. 日期设置:把 cl 这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 setTime()

    \n
    1
    2
    Date d2 = new Date(0);
    cl.setTime(d2);
    \n
  6. \n
\n

翻日历

\n

add方法,在原日期上增加(add) 年/月/日
set方法,直接设置(set) 年/月/日

\n
\n
    \n
  1. 创建日历对象

    \n
    1
    Calendar c = Calendar.getInstance();
    \n
  2. \n
  3. 获取当前日期

    \n
    1
    Date now = c.getTime();
    \n
  4. \n
  5. 把日历改成下个月的今天

    \n
    1
    2
    3
    4
    c.setTime(now);           //表示把日历改为当前时间

    c.add(Calendar.MONTH, 1);
    //在当前时间now的基础上修改日历,这里修改了月份,下个月 = 本月 + 1
    \n
  6. \n
  7. 把日历改成去年的今天

    \n
    1
    2
    c.setTime(now);
    c.add(Calendar.YEAR, -1); //负数表示以前的,去年 = 今年 - 1
    \n
  8. \n
  9. 把日历直接设置成上个月的第三天

    \n
    1
    2
    3
    4
    5
    6
    c.setTime(now);
    c.add(Calendar.MONTH, -1); //先改月份
    c.set(Calendar.DATE, 3);\t\t//再改日数,注意用set

    //如果是今天后的第三天,则用:
    //c.add(Calendar.DATE, 3)
    \n
  10. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

日期

在Java中使用日期,需导入 java.util.Date

\n
1
import java.util.Date;
\n

创建一个日期对象

    \n
  • Date date = new Date();
  • \n
\n
1
2
3
4
Date now = new Date(); 

//不加参数表示当前时间
//加了参数n表示从1970-01-01 8:00:00开始经历了n毫秒
\n
    \n
  • 日期对象转字符串对象:
  • \n
\n
1
String s = now.toString()
\n

getTime()方法

getTime()是Date()对象的一个方法,返回类型 long,用于获取从1970-01-01 8:00:00开始到现在经历的毫秒

\n

另外,Date().getTime()System.currentTimeMillis() 是一样的

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package date;

//
import java.util.Date;

public class TestDate {

public static void main(String[] args) {
Date now= new Date();

//当前日期的毫秒数
System.out.println("Date.getTime() \\t\\t\\t返回值: "+now.getTime());

//通过System.currentTimeMillis()获取当前日期的毫秒数
System.out.println("System.currentTimeMillis() \\t返回值: "+System.currentTimeMillis());

}
}

/*输出:

Date.getTime() 返回值: 1611591854927
System.currentTimeMillis() 返回值: 1611591854927

*/

\n

格式化日期并转为字符串

格式化日期:

\n

需要用到 SimpleDateFormat

\n
1
import java.text.SimpleDateFormat;
\n
    \n
  1. 指定格式

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );

    //y 代表年
    //M 代表月
    //d 代表日
    //H 代表24进制的小时
    //h 代表12进制的小时
    //m 代表分钟
    //s 代表秒
    //S 代表毫秒

    // 以上7种可以只选其中一种或多种,根据自己需要来,如:
    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd" );
    SimpleDateFormat sdf2 = new SimpleDateFormat("MM/dd" );
    \n
  2. \n
  3. 利用sdf对象的 format方法对日期 Date()进行格式化,并返回格式化后的字符串

    \n
    1
    2
    Date d = new Date();
    String str = sdf.format(d); //这样日期就按格式转为了字符串
    \n
  4. \n
\n

字符串转日期

三个步骤 :

\n
    \n
  1. 指定字符串格式:

    \n
    1
    2
    3
    SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" );

    //SimpleDateFormat sdf1 =new SimpleDateFormat("yyyy/MM/dd" );
    \n
  2. \n
  3. 创建字符串,注意格式要与 sdf 一致

    \n
    1
    2
    3
    String str = "2019-1-15 16:02:27";

    //String str1 = "2019-1-15";
    \n
  4. \n
  5. 创建日期对象,用 sdf的 parse(str)方法来把字符串转为日期对象

    \n
    1
    Date d = sdf.parse(str);
    \n
  6. \n
\n

日历 Calendar

使用前需要导入:

\n
1
import java.util.Calendar;
\n
    \n
  1. 获取日历对象 Calendar.getInstance();

    \n
    1
    Calendar cl = Calendar.getInstance();
    \n
  2. \n
  3. 通过日历对象得到日期对象: getTime()

    \n
    1
    Date d = cl.getTime();
    \n
  4. \n
  5. 日期设置:把 cl 这个日历,调成日期(时间原点) —— 1970.1.1 08:00:00,用 setTime()

    \n
    1
    2
    Date d2 = new Date(0);
    cl.setTime(d2);
    \n
  6. \n
\n

翻日历

\n

add方法,在原日期上增加(add) 年/月/日
set方法,直接设置(set) 年/月/日

\n
\n
    \n
  1. 创建日历对象

    \n
    1
    Calendar c = Calendar.getInstance();
    \n
  2. \n
  3. 获取当前日期

    \n
    1
    Date now = c.getTime();
    \n
  4. \n
  5. 把日历改成下个月的今天

    \n
    1
    2
    3
    4
    c.setTime(now);           //表示把日历改为当前时间

    c.add(Calendar.MONTH, 1);
    //在当前时间now的基础上修改日历,这里修改了月份,下个月 = 本月 + 1
    \n
  6. \n
  7. 把日历改成去年的今天

    \n
    1
    2
    c.setTime(now);
    c.add(Calendar.YEAR, -1); //负数表示以前的,去年 = 今年 - 1
    \n
  8. \n
  9. 把日历直接设置成上个月的第三天

    \n
    1
    2
    3
    4
    5
    6
    c.setTime(now);
    c.add(Calendar.MONTH, -1); //先改月份
    c.set(Calendar.DATE, 3);\t\t//再改日数,注意用set

    //如果是今天后的第三天,则用:
    //c.add(Calendar.DATE, 3)
    \n
  10. \n
\n"},{"title":"17-容器","abbrlink":"c0626013","date":"2021-02-17T04:31:15.000Z","description":"java的集合(容器)介绍,关于这一内容写了2篇笔记,看完不是很理解可以看另一篇","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n# 容 器\n\n## Collectiion接口\n\nCollection接口声明了以下方法:\n\n```java\nboolean add(Object element);\nboolean remove(Object element);\nboolean contains(Object element);\n\nint size(); //容器中元素数量 \n\nboolean isEmpty();\nvoid clear();\n\nIterator iterator();\nboolean containsAll(Collection c);\nboolean addAll(Collection c);\n\nboolean removeAll(Collection c);\nboolean retainAll(Collection c); //移除非交集元素,保留交集元素\n\nObject[] toArray(); //转换成Object数组\n```\n\n\n\n> 注:List和Set是Collection的子接口\n\n\n\n### List接口\n\nList特点:有序、元素可重复\n\n- 有序:每个元素都有索引标记\n- List通常允许满足 `e1.equals(e2)` 的元素重复加入容器\n\n相比Collection接口,List中多了一些与索引有关的方法\n\n```java\nvoid add(int index, Object element);\nObject set(int index, Object element);\nObject get(int index);\nObject remove(int index);\n\nint indexOf(Object o); //返回第一个匹配元素的索引,若没有返回-1\nint lastIndexOf(Object o); //返回最后一个匹配的元素的索引,若没有返回-1\n\n```\n\n\n\n\n\n### ArrayList类\n\nArrayList底层是用【数组】实现的存储。==特点:查询效率高,增删效率低,线程不安全==。\n\n我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?\n\n本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 \n\nArrayList的Object数组【初始化长度为10】,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中\n\n\n\n### LinkList类\n\nLinkList底层用【双向链表】实现\n\n==特点:查询效率低、增删效率高,线程不安全==\n\n> 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 \n>\n> 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。\n\n\n\n### Vector类\n\nVector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。\n\n> 如何选用ArrayList、LinkedList、Vector\n\n1. 需要线程安全时,用Vector。\n\n2. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。\n\n3. 不存在线程安全问题时,增加或删除元素较多用LinkedList。\n\n\n\n## Map接口\n\n Map就是用来存储“**键(key)-值(value) 对**”的。 Map类中存储的“键值对”通过键来标识,所以“键”不能重复。\n\n\n\nMap接口常用方法:\n\n```java\nObject put(Object key, Object value);\nObject get(Object key);\nObject remove(Object key);\n\nboolean containsKey(Object Key);\nboolean containsValue(Object value);\n\nint size();\nboolean isEmpty();\n\nvoid putAll(Map t);\nvoid clear();\n```\n\n Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。\n\n\n\n### HashMap类\n\nHashMap采用**哈希算法**实现,是Map接口最常用的实现类。\n\n由于底层采用了哈希表存储数据,我们要求**键不能重复**,==如果发生重复,新的键值对会替换旧的键值对==\n\nHashMap在查找、删除、修改方面都有非常高的效率\n\n\n\n> HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。\n>\n> **HashMap与HashTable的区别**\n>\n> 1. HashMap: 线程不安全,效率高。**允许key或value为null**\n>\n> 2. HashTable: 线程安全,效率低。**不允许key或value为null**\n\n\n\n## Set接口与HashSet类\n\nSet接口继承自Collection,Set接口中**没有新增方法**,方法和Collection保持完全一致。\n\nSet容器特点:无序、不可重复\n\n> 新元素如果和Set中某个元素通过`equals()`方法对比为true,则不能加入;\n>\n> 甚至,Set中也只能放入一个null元素,不能多个。\n\nSet常用的**实现类**有:HashSet、TreeSet等,我们一般使用HashSet\n\n\n\nHashSet基本用法\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Set s = new HashSet();\n s.add(\"hello\");\n s.add(\"world\");\n System.out.println(s);\n s.add(\"hello\"); //相同的元素不会被加入\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n }\n}\n```\n\n\n\n\n\n## 迭代器\n\n\n\n将容器转为迭代器:\n\n```java\nIterator iter = aList.iterator()\n```\n\n使用Iterator迭代器遍历容器元素(List/Set/Map)\n\n- 迭代器遍历ArrayList\n\n```java\nList aList = new ArrayList();\n\nfor (int i = 0; i < 5; i++) {\n aList.add(\"a\" + i);\n}\n\nfor (Iterator iter = aList.iterator(); iter.hasNext();){\n String temp = iter.next();\n System.out.print(temp + \"\\t\");\n if (temp.endsWith(\"3\")) {// 删除3结尾的字符串\n iter.remove();\n }\n}\n```\n\n\n\n- 迭代器遍历Map 【1】\n\n```java\npublic class Test{\n public static void main(String[] args){\n Map map = new HashMap();\n map.put(\"A\", \"GaoQi\");\n map.put(\"B\", \"LiuBa\");\n Set> ss = map.entrySet();\n for(Iterator> iterator = ss.iterator(); iterator.hasNext();){\n Entry e = iterator.next();\n System.out.println(e.getKey() + \"--\" + e.getValue());\n }\n }\n}\n```\n\n\n\n- 迭代器遍历Map 【2】\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Map map = new HashMap();\n map.put(\"A\", \"GaoQi\");\n map.put(\"B\", \"LiuBa\");\n Set ss = map.keySet();\n for (Iterator iterator = ss.iterator(); iterator.hasNext();) {\n String key = iterator.next();\n System.out.println(key + \"--\" + map.get(key));\n }\n }\n}\n```\n\n\n\n\n\n## Collections工具类\n\n类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。\n\n1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。\n2. void shuffle(List) //对List容器内的元素进行随机排列。\n3. void reverse(List) //对List容器内的元素进行逆续排列 。\n4. void fill(List, Object) //用一个特定的对象重写整个List容器。\n5. int binarySearch(List, Object) //对于顺序的List容器,采用折半查找的方法查找特定对象。\n\n","source":"_posts/javaSE/17-容器.md","raw":"---\ntitle: 17-容器\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: c0626013\ndate: 2021-02-17 12:31:15\ndescription: java的集合(容器)介绍,关于这一内容写了2篇笔记,看完不是很理解可以看另一篇\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n# 容 器\n\n## Collectiion接口\n\nCollection接口声明了以下方法:\n\n```java\nboolean add(Object element);\nboolean remove(Object element);\nboolean contains(Object element);\n\nint size(); //容器中元素数量 \n\nboolean isEmpty();\nvoid clear();\n\nIterator iterator();\nboolean containsAll(Collection c);\nboolean addAll(Collection c);\n\nboolean removeAll(Collection c);\nboolean retainAll(Collection c); //移除非交集元素,保留交集元素\n\nObject[] toArray(); //转换成Object数组\n```\n\n\n\n> 注:List和Set是Collection的子接口\n\n\n\n### List接口\n\nList特点:有序、元素可重复\n\n- 有序:每个元素都有索引标记\n- List通常允许满足 `e1.equals(e2)` 的元素重复加入容器\n\n相比Collection接口,List中多了一些与索引有关的方法\n\n```java\nvoid add(int index, Object element);\nObject set(int index, Object element);\nObject get(int index);\nObject remove(int index);\n\nint indexOf(Object o); //返回第一个匹配元素的索引,若没有返回-1\nint lastIndexOf(Object o); //返回最后一个匹配的元素的索引,若没有返回-1\n\n```\n\n\n\n\n\n### ArrayList类\n\nArrayList底层是用【数组】实现的存储。==特点:查询效率高,增删效率低,线程不安全==。\n\n我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?\n\n本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 \n\nArrayList的Object数组【初始化长度为10】,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中\n\n\n\n### LinkList类\n\nLinkList底层用【双向链表】实现\n\n==特点:查询效率低、增删效率高,线程不安全==\n\n> 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 \n>\n> 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。\n\n\n\n### Vector类\n\nVector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。\n\n> 如何选用ArrayList、LinkedList、Vector\n\n1. 需要线程安全时,用Vector。\n\n2. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。\n\n3. 不存在线程安全问题时,增加或删除元素较多用LinkedList。\n\n\n\n## Map接口\n\n Map就是用来存储“**键(key)-值(value) 对**”的。 Map类中存储的“键值对”通过键来标识,所以“键”不能重复。\n\n\n\nMap接口常用方法:\n\n```java\nObject put(Object key, Object value);\nObject get(Object key);\nObject remove(Object key);\n\nboolean containsKey(Object Key);\nboolean containsValue(Object value);\n\nint size();\nboolean isEmpty();\n\nvoid putAll(Map t);\nvoid clear();\n```\n\n Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。\n\n\n\n### HashMap类\n\nHashMap采用**哈希算法**实现,是Map接口最常用的实现类。\n\n由于底层采用了哈希表存储数据,我们要求**键不能重复**,==如果发生重复,新的键值对会替换旧的键值对==\n\nHashMap在查找、删除、修改方面都有非常高的效率\n\n\n\n> HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。\n>\n> **HashMap与HashTable的区别**\n>\n> 1. HashMap: 线程不安全,效率高。**允许key或value为null**\n>\n> 2. HashTable: 线程安全,效率低。**不允许key或value为null**\n\n\n\n## Set接口与HashSet类\n\nSet接口继承自Collection,Set接口中**没有新增方法**,方法和Collection保持完全一致。\n\nSet容器特点:无序、不可重复\n\n> 新元素如果和Set中某个元素通过`equals()`方法对比为true,则不能加入;\n>\n> 甚至,Set中也只能放入一个null元素,不能多个。\n\nSet常用的**实现类**有:HashSet、TreeSet等,我们一般使用HashSet\n\n\n\nHashSet基本用法\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Set s = new HashSet();\n s.add(\"hello\");\n s.add(\"world\");\n System.out.println(s);\n s.add(\"hello\"); //相同的元素不会被加入\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n s.add(null);\n System.out.println(s);\n }\n}\n```\n\n\n\n\n\n## 迭代器\n\n\n\n将容器转为迭代器:\n\n```java\nIterator iter = aList.iterator()\n```\n\n使用Iterator迭代器遍历容器元素(List/Set/Map)\n\n- 迭代器遍历ArrayList\n\n```java\nList aList = new ArrayList();\n\nfor (int i = 0; i < 5; i++) {\n aList.add(\"a\" + i);\n}\n\nfor (Iterator iter = aList.iterator(); iter.hasNext();){\n String temp = iter.next();\n System.out.print(temp + \"\\t\");\n if (temp.endsWith(\"3\")) {// 删除3结尾的字符串\n iter.remove();\n }\n}\n```\n\n\n\n- 迭代器遍历Map 【1】\n\n```java\npublic class Test{\n public static void main(String[] args){\n Map map = new HashMap();\n map.put(\"A\", \"GaoQi\");\n map.put(\"B\", \"LiuBa\");\n Set> ss = map.entrySet();\n for(Iterator> iterator = ss.iterator(); iterator.hasNext();){\n Entry e = iterator.next();\n System.out.println(e.getKey() + \"--\" + e.getValue());\n }\n }\n}\n```\n\n\n\n- 迭代器遍历Map 【2】\n\n```java\npublic class Test {\n public static void main(String[] args) {\n Map map = new HashMap();\n map.put(\"A\", \"GaoQi\");\n map.put(\"B\", \"LiuBa\");\n Set ss = map.keySet();\n for (Iterator iterator = ss.iterator(); iterator.hasNext();) {\n String key = iterator.next();\n System.out.println(key + \"--\" + map.get(key));\n }\n }\n}\n```\n\n\n\n\n\n## Collections工具类\n\n类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。\n\n1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。\n2. void shuffle(List) //对List容器内的元素进行随机排列。\n3. void reverse(List) //对List容器内的元素进行逆续排列 。\n4. void fill(List, Object) //用一个特定的对象重写整个List容器。\n5. int binarySearch(List, Object) //对于顺序的List容器,采用折半查找的方法查找特定对象。\n\n","slug":"javaSE/17-容器","published":1,"updated":"2021-08-29T15:31:56.883Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qr0063akvedrb617kz","content":"

容 器

Collectiion接口

Collection接口声明了以下方法:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
boolean add(Object element);
boolean remove(Object element);
boolean contains(Object element);

int size(); //容器中元素数量

boolean isEmpty();
void clear();

Iterator iterator();
boolean containsAll(Collection c);
boolean addAll(Collection c);

boolean removeAll(Collection c);
boolean retainAll(Collection c); //移除非交集元素,保留交集元素

Object[] toArray(); //转换成Object数组
\n
\n

注:List和Set是Collection的子接口

\n
\n

List接口

List特点:有序、元素可重复

\n
    \n
  • 有序:每个元素都有索引标记
  • \n
  • List通常允许满足 e1.equals(e2) 的元素重复加入容器
  • \n
\n

相比Collection接口,List中多了一些与索引有关的方法

\n
1
2
3
4
5
6
7
8
void add(int index, Object element);
Object set(int index, Object element);
Object get(int index);
Object remove(int index);

int indexOf(Object o); //返回第一个匹配元素的索引,若没有返回-1
int lastIndexOf(Object o); //返回最后一个匹配的元素的索引,若没有返回-1

\n

ArrayList类

ArrayList底层是用【数组】实现的存储。==特点:查询效率高,增删效率低,线程不安全==。

\n

我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?

\n

本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。

\n

ArrayList的Object数组【初始化长度为10】,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中

\n

LinkList类

LinkList底层用【双向链表】实现

\n

==特点:查询效率低、增删效率高,线程不安全==

\n
\n

双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。

\n

所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。

\n
\n

Vector类

Vector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。

\n
\n

如何选用ArrayList、LinkedList、Vector

\n
\n
    \n
  1. 需要线程安全时,用Vector。

    \n
  2. \n
  3. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。

    \n
  4. \n
  5. 不存在线程安全问题时,增加或删除元素较多用LinkedList。

    \n
  6. \n
\n

Map接口

Map就是用来存储“键(key)-值(value) 对”的。 Map类中存储的“键值对”通过键来标识,所以“键”不能重复。

\n

Map接口常用方法:

\n
1
2
3
4
5
6
7
8
9
10
11
12
Object put(Object key, Object value);
Object get(Object key);
Object remove(Object key);

boolean containsKey(Object Key);
boolean containsValue(Object value);

int size();
boolean isEmpty();

void putAll(Map t);
void clear();
\n

Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。

\n

HashMap类

HashMap采用哈希算法实现,是Map接口最常用的实现类。

\n

由于底层采用了哈希表存储数据,我们要求键不能重复,==如果发生重复,新的键值对会替换旧的键值对==

\n

HashMap在查找、删除、修改方面都有非常高的效率

\n
\n

HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。

\n

HashMap与HashTable的区别

\n
    \n
  1. HashMap: 线程不安全,效率高。允许key或value为null

    \n
  2. \n
  3. HashTable: 线程安全,效率低。不允许key或value为null

    \n
  4. \n
\n
\n

Set接口与HashSet类

Set接口继承自Collection,Set接口中没有新增方法,方法和Collection保持完全一致。

\n

Set容器特点:无序、不可重复

\n
\n

新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入;

\n

甚至,Set中也只能放入一个null元素,不能多个。

\n
\n

Set常用的实现类有:HashSet、TreeSet等,我们一般使用HashSet

\n

HashSet基本用法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args) {
Set<String> s = new HashSet<String>();
s.add("hello");
s.add("world");
System.out.println(s);
s.add("hello"); //相同的元素不会被加入
System.out.println(s);
s.add(null);
System.out.println(s);
s.add(null);
System.out.println(s);
}
}
\n

迭代器

将容器转为迭代器:

\n
1
Iterator<E> iter = aList.iterator()
\n

使用Iterator迭代器遍历容器元素(List/Set/Map)

\n
    \n
  • 迭代器遍历ArrayList
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
List<String> aList = new ArrayList<String>();

for (int i = 0; i < 5; i++) {
aList.add("a" + i);
}

for (Iterator<String> iter = aList.iterator(); iter.hasNext();){
String temp = iter.next();
System.out.print(temp + "\\t");
if (temp.endsWith("3")) {// 删除3结尾的字符串
iter.remove();
}
}
\n
    \n
  • 迭代器遍历Map 【1】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class Test{
public static void main(String[] args){
Map<String, String> map = new HashMap<String String>();
map.put("A", "GaoQi");
map.put("B", "LiuBa");
Set<Entry<String, String>> ss = map.entrySet();
for(Iterator<Entry<String, String>> iterator = ss.iterator(); iterator.hasNext();){
Entry<String, String> e = iterator.next();
System.out.println(e.getKey() + "--" + e.getValue());
}
}
}
\n
    \n
  • 迭代器遍历Map 【2】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("A", "GaoQi");
map.put("B", "LiuBa");
Set<String> ss = map.keySet();
for (Iterator<String> iterator = ss.iterator(); iterator.hasNext();) {
String key = iterator.next();
System.out.println(key + "--" + map.get(key));
}
}
}
\n

Collections工具类

类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。

\n
    \n
  1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
  2. \n
  3. void shuffle(List) //对List容器内的元素进行随机排列。
  4. \n
  5. void reverse(List) //对List容器内的元素进行逆续排列 。
  6. \n
  7. void fill(List, Object) //用一个特定的对象重写整个List容器。
  8. \n
  9. int binarySearch(List, Object) //对于顺序的List容器,采用折半查找的方法查找特定对象。
  10. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

容 器

Collectiion接口

Collection接口声明了以下方法:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
boolean add(Object element);
boolean remove(Object element);
boolean contains(Object element);

int size(); //容器中元素数量

boolean isEmpty();
void clear();

Iterator iterator();
boolean containsAll(Collection c);
boolean addAll(Collection c);

boolean removeAll(Collection c);
boolean retainAll(Collection c); //移除非交集元素,保留交集元素

Object[] toArray(); //转换成Object数组
\n
\n

注:List和Set是Collection的子接口

\n
\n

List接口

List特点:有序、元素可重复

\n
    \n
  • 有序:每个元素都有索引标记
  • \n
  • List通常允许满足 e1.equals(e2) 的元素重复加入容器
  • \n
\n

相比Collection接口,List中多了一些与索引有关的方法

\n
1
2
3
4
5
6
7
8
void add(int index, Object element);
Object set(int index, Object element);
Object get(int index);
Object remove(int index);

int indexOf(Object o); //返回第一个匹配元素的索引,若没有返回-1
int lastIndexOf(Object o); //返回最后一个匹配的元素的索引,若没有返回-1

\n

ArrayList类

ArrayList底层是用【数组】实现的存储。==特点:查询效率高,增删效率低,线程不安全==。

\n

我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?

\n

本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。

\n

ArrayList的Object数组【初始化长度为10】,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中

\n

LinkList类

LinkList底层用【双向链表】实现

\n

==特点:查询效率低、增删效率高,线程不安全==

\n
\n

双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。

\n

所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。

\n
\n

Vector类

Vector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”。 比如,indexOf方法就增加了synchronized同步标记。

\n
\n

如何选用ArrayList、LinkedList、Vector

\n
\n
    \n
  1. 需要线程安全时,用Vector。

    \n
  2. \n
  3. 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)。

    \n
  4. \n
  5. 不存在线程安全问题时,增加或删除元素较多用LinkedList。

    \n
  6. \n
\n

Map接口

Map就是用来存储“键(key)-值(value) 对”的。 Map类中存储的“键值对”通过键来标识,所以“键”不能重复。

\n

Map接口常用方法:

\n
1
2
3
4
5
6
7
8
9
10
11
12
Object put(Object key, Object value);
Object get(Object key);
Object remove(Object key);

boolean containsKey(Object Key);
boolean containsValue(Object value);

int size();
boolean isEmpty();

void putAll(Map t);
void clear();
\n

Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。

\n

HashMap类

HashMap采用哈希算法实现,是Map接口最常用的实现类。

\n

由于底层采用了哈希表存储数据,我们要求键不能重复,==如果发生重复,新的键值对会替换旧的键值对==

\n

HashMap在查找、删除、修改方面都有非常高的效率

\n
\n

HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。

\n

HashMap与HashTable的区别

\n
    \n
  1. HashMap: 线程不安全,效率高。允许key或value为null

    \n
  2. \n
  3. HashTable: 线程安全,效率低。不允许key或value为null

    \n
  4. \n
\n
\n

Set接口与HashSet类

Set接口继承自Collection,Set接口中没有新增方法,方法和Collection保持完全一致。

\n

Set容器特点:无序、不可重复

\n
\n

新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入;

\n

甚至,Set中也只能放入一个null元素,不能多个。

\n
\n

Set常用的实现类有:HashSet、TreeSet等,我们一般使用HashSet

\n

HashSet基本用法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args) {
Set<String> s = new HashSet<String>();
s.add("hello");
s.add("world");
System.out.println(s);
s.add("hello"); //相同的元素不会被加入
System.out.println(s);
s.add(null);
System.out.println(s);
s.add(null);
System.out.println(s);
}
}
\n

迭代器

将容器转为迭代器:

\n
1
Iterator<E> iter = aList.iterator()
\n

使用Iterator迭代器遍历容器元素(List/Set/Map)

\n
    \n
  • 迭代器遍历ArrayList
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
List<String> aList = new ArrayList<String>();

for (int i = 0; i < 5; i++) {
aList.add("a" + i);
}

for (Iterator<String> iter = aList.iterator(); iter.hasNext();){
String temp = iter.next();
System.out.print(temp + "\\t");
if (temp.endsWith("3")) {// 删除3结尾的字符串
iter.remove();
}
}
\n
    \n
  • 迭代器遍历Map 【1】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class Test{
public static void main(String[] args){
Map<String, String> map = new HashMap<String String>();
map.put("A", "GaoQi");
map.put("B", "LiuBa");
Set<Entry<String, String>> ss = map.entrySet();
for(Iterator<Entry<String, String>> iterator = ss.iterator(); iterator.hasNext();){
Entry<String, String> e = iterator.next();
System.out.println(e.getKey() + "--" + e.getValue());
}
}
}
\n
    \n
  • 迭代器遍历Map 【2】
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("A", "GaoQi");
map.put("B", "LiuBa");
Set<String> ss = map.keySet();
for (Iterator<String> iterator = ss.iterator(); iterator.hasNext();) {
String key = iterator.next();
System.out.println(key + "--" + map.get(key));
}
}
}
\n

Collections工具类

类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。

\n
    \n
  1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
  2. \n
  3. void shuffle(List) //对List容器内的元素进行随机排列。
  4. \n
  5. void reverse(List) //对List容器内的元素进行逆续排列 。
  6. \n
  7. void fill(List, Object) //用一个特定的对象重写整个List容器。
  8. \n
  9. int binarySearch(List, Object) //对于顺序的List容器,采用折半查找的方法查找特定对象。
  10. \n
\n"},{"title":"2.1-类和对象的属性初始化","abbrlink":"4515cdfc","date":"2021-02-06T10:25:41.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 属性初始化\n\n## 对象属性初始化\n\n- 声明的同时初始化\n- 块初始化(声明后初始化)\n- 通过构造方法初始化\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name = \"some hero\"; //声明该属性的时候初始化\n protected float hp;\n float maxHP;\n \n {\n maxHP = 200; //块初始化\n } \n \n public Hero(){\n hp = 100; //构造方法中初始化\n \n }\n \n}\n```\n\n注意:最后进行的是通过**构造方法进行的初始化**,需要在创建对象的时候才开始初始化\n\n```java\npackage ChuShiHua;\n\npublic class Hero {\n public String name = \"some hero\";\n {\n System.out.println(\"声明时初始化:\"+name);\n }\n\n public Hero() {\n name = \"one hero\";\n System.out.println(\"构造方法中初始化:\" + name);\n }\n\n {\n name = \"the hero\";\n System.out.println(\"块初始化:\" + name);\n }\n}\n```\n\n创建对象,查看输出:\n\n```java\npackage ChuShiHua;\n\npublic class Test {\n public static void main(String[] args){\n Hero hero = new Hero();\n }\n \n}\n\n/**输出:\n声明时初始化:some hero\n块初始化:the hero\n构造方法中初始化:one hero\n*/\n```\n\n\n\n因此初始化顺序为:声明时初始化->块初始化->构造方法中初始化\n\n## 类属性初始化\n\n- 声明的同时初始化\n- **静态**块初始化\n\n```java\npackage charactor;\n \npublic class Hero {\n\n public static int itemCapacity=8; //声明的时候 初始化\n \n static{ //静态初始化块 \n itemCapacity = 6;\n }\n \n public Hero(){\n \n }\n \n public static void main(String[] args) {\n System.out.println(Hero.itemCapacity);\n }\n \n}\n```\n\n","source":"_posts/javaSE/2.1-类和对象的属性初始化.md","raw":"---\ntitle: 2.1-类和对象的属性初始化\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 4515cdfc\ndate: 2021-02-06 18:25:41\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 属性初始化\n\n## 对象属性初始化\n\n- 声明的同时初始化\n- 块初始化(声明后初始化)\n- 通过构造方法初始化\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name = \"some hero\"; //声明该属性的时候初始化\n protected float hp;\n float maxHP;\n \n {\n maxHP = 200; //块初始化\n } \n \n public Hero(){\n hp = 100; //构造方法中初始化\n \n }\n \n}\n```\n\n注意:最后进行的是通过**构造方法进行的初始化**,需要在创建对象的时候才开始初始化\n\n```java\npackage ChuShiHua;\n\npublic class Hero {\n public String name = \"some hero\";\n {\n System.out.println(\"声明时初始化:\"+name);\n }\n\n public Hero() {\n name = \"one hero\";\n System.out.println(\"构造方法中初始化:\" + name);\n }\n\n {\n name = \"the hero\";\n System.out.println(\"块初始化:\" + name);\n }\n}\n```\n\n创建对象,查看输出:\n\n```java\npackage ChuShiHua;\n\npublic class Test {\n public static void main(String[] args){\n Hero hero = new Hero();\n }\n \n}\n\n/**输出:\n声明时初始化:some hero\n块初始化:the hero\n构造方法中初始化:one hero\n*/\n```\n\n\n\n因此初始化顺序为:声明时初始化->块初始化->构造方法中初始化\n\n## 类属性初始化\n\n- 声明的同时初始化\n- **静态**块初始化\n\n```java\npackage charactor;\n \npublic class Hero {\n\n public static int itemCapacity=8; //声明的时候 初始化\n \n static{ //静态初始化块 \n itemCapacity = 6;\n }\n \n public Hero(){\n \n }\n \n public static void main(String[] args) {\n System.out.println(Hero.itemCapacity);\n }\n \n}\n```\n\n","slug":"javaSE/2.1-类和对象的属性初始化","published":1,"updated":"2021-08-29T15:30:34.836Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qs0066akve3ppb6e1q","content":"

属性初始化

对象属性初始化

    \n
  • 声明的同时初始化
  • \n
  • 块初始化(声明后初始化)
  • \n
  • 通过构造方法初始化
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package charactor;

public class Hero {
public String name = "some hero"; //声明该属性的时候初始化
protected float hp;
float maxHP;

{
maxHP = 200; //块初始化
}

public Hero(){
hp = 100; //构造方法中初始化

}

}
\n

注意:最后进行的是通过构造方法进行的初始化,需要在创建对象的时候才开始初始化

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package ChuShiHua;

public class Hero {
public String name = "some hero";
{
System.out.println("声明时初始化:"+name);
}

public Hero() {
name = "one hero";
System.out.println("构造方法中初始化:" + name);
}

{
name = "the hero";
System.out.println("块初始化:" + name);
}
}
\n

创建对象,查看输出:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package ChuShiHua;

public class Test {
public static void main(String[] args){
Hero hero = new Hero();
}

}

/**输出:
声明时初始化:some hero
块初始化:the hero
构造方法中初始化:one hero
*/
\n

因此初始化顺序为:声明时初始化->块初始化->构造方法中初始化

\n

类属性初始化

    \n
  • 声明的同时初始化
  • \n
  • 静态块初始化
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package charactor;

public class Hero {

public static int itemCapacity=8; //声明的时候 初始化

static{ //静态初始化块
itemCapacity = 6;
}

public Hero(){

}

public static void main(String[] args) {
System.out.println(Hero.itemCapacity);
}

}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

属性初始化

对象属性初始化

    \n
  • 声明的同时初始化
  • \n
  • 块初始化(声明后初始化)
  • \n
  • 通过构造方法初始化
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package charactor;

public class Hero {
public String name = "some hero"; //声明该属性的时候初始化
protected float hp;
float maxHP;

{
maxHP = 200; //块初始化
}

public Hero(){
hp = 100; //构造方法中初始化

}

}
\n

注意:最后进行的是通过构造方法进行的初始化,需要在创建对象的时候才开始初始化

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package ChuShiHua;

public class Hero {
public String name = "some hero";
{
System.out.println("声明时初始化:"+name);
}

public Hero() {
name = "one hero";
System.out.println("构造方法中初始化:" + name);
}

{
name = "the hero";
System.out.println("块初始化:" + name);
}
}
\n

创建对象,查看输出:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package ChuShiHua;

public class Test {
public static void main(String[] args){
Hero hero = new Hero();
}

}

/**输出:
声明时初始化:some hero
块初始化:the hero
构造方法中初始化:one hero
*/
\n

因此初始化顺序为:声明时初始化->块初始化->构造方法中初始化

\n

类属性初始化

    \n
  • 声明的同时初始化
  • \n
  • 静态块初始化
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package charactor;

public class Hero {

public static int itemCapacity=8; //声明的时候 初始化

static{ //静态初始化块
itemCapacity = 6;
}

public Hero(){

}

public static void main(String[] args) {
System.out.println(Hero.itemCapacity);
}

}
\n"},{"title":"17-集合框架","abbrlink":"9dc8e4af","date":"2021-02-17T03:31:29.000Z","description":"java的集合(容器)介绍,关于这一内容写了2篇笔记,看完不是很理解可以看另一篇《容器》","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 集合框架\n\n## ArrayList类\n\n### 容器概念\n\n我们已经知道,如果要存放多个对象,可以使用数组,但是数组有局限性,比如:\n\n声明长度是10的数组,不用的数组就浪费了,超过10的个数,又放不下\n\n为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是`ArrayList`,[容器的容量](https://how2j.cn/k/number-string/number-string-stringbuilder/328.html#step724)\"capacity\"会随着对象的增加,自动增长,只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。\n\n使用`ArrayList`前需导入:\n\n```java\nimport java.util.ArrayList;\n```\n\n```java\n//容器类ArrayList,用于存放对象\nArrayList heros = new ArrayList();\nheros.add( new Hero(\"盖伦\"));\nSystem.out.println(heros.size());\n\n//容器的容量\"capacity\"会随着对象的增加,自动增长\n//只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。\nheros.add( new Hero(\"提莫\"));\n```\n\n\n\n### ArrayList常用方法\n\n| 关键字 | 简介 | 示例代码 |\n| :------- | :--------------------------- | :-------------------: |\n| add | 增加 | [示例代码](#add) |\n| contains | 判断是否存在 | [示例代码](#contains) |\n| get | 获取指定位置的对象 | [示例代码](#get) |\n| indexOf | 获取对象所处的位置 | [示例代码](#indexOf) |\n| remove | 删除 | [示例代码](#remove) |\n| set | 替换 | [示例代码](#set) |\n| size | 获取大小 | [示例代码](#size) |\n| toArray | 转换为数组 | [示例代码](#toArray) |\n| addAll | 把另一个容器所有对象都加进来 | [示例代码](#addAll) |\n| clear | 清空 | [示例代码](#clear) |\n\n- ##### `add`\n\n ```java\n for (int i = 0; i < 5; i++) {\n \theros.add(new Hero(\"hero \" + i));\n }\n Hero specialHero = new Hero(\"special hero\");\n heros.add(specialHero);\n ```\n\n- ##### `contains`\n\n ```java\n // 初始化5个对象\n System.out.println(heros); //打印heros\n System.out.println(heros.contains(new Hero(\"hero 1\")));\t //false\n System.out.println(heros.contains(specialHero));\t\t//true\n ```\n\n- ##### `get`\n\n 通过get获取指定位置的对象,如果输入的下标越界,一样会报错\n\n ```java\n System.out.println(heros.get(5)); //获取指定位置的对象\n \n System.out.println(heros.get(6)); //如果超出了范围,依然会报错\n ```\n\n- ##### `indexOf`\n\n ```java\n System.out.println(\"specialHero所处的位置:\"+heros.indexOf(specialHero));\n \n System.out.println(\"新英雄,但是名字是\\\"hero 1\\\"所处的位置:\"+heros.indexOf(new Hero(\"hero 1\")));\n ```\n\n- ##### `remove`\n\n - remove可以根据下标删除ArrayList的元素\n\n ```java\n heros.remove(2);\n ```\n\n - 也可以根据对象删除\n\n ```java\n heros.remove(specialHero);\n ```\n\n- ##### `set`\n\n **set**用于替换指定位置的元素\n\n ```java\n heros.set(5, new Hero(\"hero 5\"));\n ```\n\n- ##### `size`\n\n **size** 用于获取ArrayList的大小\n\n ```java\n System.out.println(heros.size());\n ```\n\n- ##### `toArray`\n\n **toArray**可以把一个ArrayList对象转换为数组。\n 需要注意的是,如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组\n\n ```java\n Hero hs[] = (Hero[])heros.toArray(new Hero[]{});\n //传递一个Hero数组类型的对象new Hero[]{}给toArray()\n ```\n\n- ##### `addAll`\n\n **addAll** 把另一个容器所有对象都加进来\n\n ```java\n ArrayList anotherHeros = new ArrayList();\n anotherHeros.add(new Hero(\"hero a\"));\n anotherHeros.add(new Hero(\"hero b\"));\n \n heros.addAll(anotherHeros);\n ```\n\n- ##### `clear`\n\n **clear** 清空一个ArrayList\n\n ```java\n heros.clear();\n System.out.println(\"ArrayList heros:\\t\" + heros);\n ```\n\n\n\n### List接口\n\n- ArrayList与List\n\n ArrayList实现了接口List,常见的写法会把引用声明为接口List类型\n\n > 注意:是**java.util.List**,而**不是**java.awt.List\n\n ```java\n package collection;\n \n import java.util.ArrayList;\n import java.util.List;\n \n import charactor.Hero;\n \n public class TestCollection {\n \n public static void main(String[] args) {\n //ArrayList实现了接口List\n \n //常见的写法会把引用声明为接口List类型\n //注意:是java.util.List,而不是java.awt.List\n //接口引用指向子类对象(多态)\n \n List heros = new ArrayList();\n heros.add( new Hero(\"盖伦\"));\n System.out.println(heros.size());\n \n }\n \n }\n ```\n\n \n\n- List的接口与方法\n\n 因为ArrayList实现了List接口,所以List接口的方法ArrayList都实现了。\n 在【[ArrayList常用方法](#ArrayList常用方法)】有详细的讲解,在此不作赘述\n\n\n\n### 泛型\n\n- 不指定泛型的容器,可以存放任何类型的元素\n- 指定了泛型的容器,只能存放指定类型的元素以及其子类\n\n语法:\n\n```java\nList genericheros = new ArrayList(); //只能存放Hero类型\n\n//简写\nList genericheros = new ArrayList<>();\n```\n\n\n\n### 遍历\n\n| 关键字 | 简介 | 示例代码 |\n| :-------- | :-------------- | :--------------------- |\n| for | 用for循环遍历 | [示例代码](#for) |\n| iterator | 迭代器遍历 | [示例代码](#iterator) |\n| 增强型for | 用增强型for循环 | [示例代码](#增强型for) |\n\n- ##### for\n\n ```java\n for (int i = 0; i < heros.size(); i++) {\n \tHero h = heros.get(i);\n \tSystem.out.println(h);\n }\n ```\n\n- ##### iterator\n\n Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下:\n\n ```java\n import java.util.Iterator; // 引入 Iterator 类\n ```\n\n 迭代器的两个基本操作是 next 、hasNext 和 remove。\n\n - 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。\n\n - 调用 it.hasNext() 用于检测集合中是否还有元素。\n\n - 调用 it.remove() 将迭代器返回的元素删除。\n\n 首先将集合转为迭代器\n\n ```java\n Iterator it= heros.iterator();\n ```\n\n 用while遍历\n\n ```java\n //从最开始的位置判断\"下一个\"位置是否有数据\n while(it.hasNext()){ \t\t//判断是否为null\n \n Hero h = it.next(); \t//通过next取出来,并且把指针向下移动\n System.out.println(h);\n }\n ```\n\n 用for遍历\n\n ```java\n for (Iterator iterator = heros.iterator(); iterator.hasNext();) {\n Hero hero = (Hero) iterator.next();\n System.out.println(hero);\n }\n ```\n\n- ##### 增强型for\n\n ```java\n for (Hero h : heros) {\n \tSystem.out.println(h);\n }\n ```\n\n\n\n## 其他集合\n\n### 链表-LinkList\n\n以下情况使用 ArrayList :\n\n- 频繁访问列表中的某一个元素。\n- 只需要在列表末尾进行添加和删除元素操作。\n\n以下情况使用 LinkedList :\n\n- 你需要通过循环迭代来访问列表中的某些元素。\n- 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。\n\n```java\n//创建链表\nimport java.util.LinkList;\n\nLinkedList list = new LinkedList(); // 普通创建方法\n//或者\nLinkedList list = new LinkedList(Collection c); // 使用集合创建链表\n```\n\n常用方法:\n\n插入:\n\n- addLast()\n- addFirst()\n\n查看:\n\n- getFirst()\n- getLast()\n\n删除:\n\n- removeFirst()\n- removeLast()\n\n\n\n### 队列-Queue\n\nQueue是先进先出队列 FIFO,常用方法:\n\n- `offer(e)` 在最后添加元素e\n- `poll()` 取出第一个元素\n- `peek()` 查看第一个元素\n\n\n\n### 二叉树\n\n二叉树由各种**节点**组成\n\n二叉树特点:\n\n- 每个节点都可以有**左子**节点,**右子**节点\t\n- 每一个节点都有一个**值**\n\n\"image-20210212204447345\"\n\n\n\n```java\npackage collection;\n \npublic class Node {\n public Node leftNode;\t// 左子节点\n \n public Node rightNode;\t// 右子节点\n \n public Object value;\t// 值\n}\n```\n\n### HashMap\n\n1. HashMap 是一个散列表,它存储的内容是键值对`key-value`映射;\n\n2. HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步;\n\n3. HashMap 是**无序**的,即不会记录插入的顺序;\n4. HashMap中的key不能重复,value可以重复;\n5. HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。\n\n\n\"image-20210212205720886\"\n\n创建一个 HashMap 对象 Sites,其中:\n\n- key为整型\n\n- value为字符串(String)类型\n\n ```java\n import java.util.HashMap; // 引入 HashMap 类\n \n HashMap Sites = new HashMap();\n ```\n\n> 添加元素 `put(key, value)`\n>\n> ```java\n> import java.util.HashMap;\n> \n> public class HashMapTest {\n> public static void main(String[] args) {\n> // 创建 HashMap 对象 Sites\n> HashMap Sites = new HashMap();\n> // 添加键值对\n> Sites.put(1, \"Google\");\n> Sites.put(2, \"Runoob\");\n> Sites.put(3, \"Taobao\");\n> Sites.put(4, \"Zhihu\");\n> System.out.println(Sites);\n> }\n> }\n> ```\n\n> 访问元素的value值:`get(key)`\n>\n> ```java\n> System.out.println(Sites.get(3));\n> ```\n\n> 删除元素(键值对):`remove(key)`\n>\n> ```java\n> Sites.remove(4);\n> ```\n> 清空所有键值对:`clear()`\n>\n> ```java\n> Sites.clear();\n> ```\n\n---\n\n> 返回元素(键值对)数量:`size()`\n>\n> ```java\n> Sites.size();\n> ```\n\n---\n\n> 返回所有key:`keySet()`\n>\n> ```java\n> Sites.keySet()\n> ```\n>\n> 返回所有values:`values()`\n>\n> ```java\n> Sites.values()\n> ```\n\n### HashSet\n\n1. HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。\n2. HashSet 允许有 null 值。\n3. HashSet 是无序的,即不会记录插入的顺序。\n4. 由于Set是无序的,所以Set不可以获取指定位置的元素。\n5. HashSet 实现了 Set 接口。\n\n\"image-20210212212148854\"\n\n创建一个Set:\n\n```java\nHashSet sites = new HashSet();\n```\n\n- 添加元素:`sites.add(e)`\n- 判断元素e是否存在:`sites.contains(e)`\n- 删除元素:`sites.remove(e)`\n- 清空元素:`sites.clear()`\n- 计算大小:`sites.size()`\n\n### Collection\n\nCollection是 Set、List、Queue、Deque的接口(Deque 继承 Queue,间接的继承了 Collection)\n\n- Queue: 先进先出队列\n- Deque: 双向链表\n\n> 注:Collection 和 Map 之间没有关系,Collection是放一个一个对象的,Map 是放键值对的\n\n### Collections\n\nCollections是一个类,**容器**的工具类,就如同Arrays是数组的工具类\n\nCollections具有以下方法:\n\n| 关键字 | 简介 |\n| :--------------- | :----------------- |\n| reverse | 反转 |\n| shuffle | 混淆 |\n| sort | 排序 |\n| swap | 交换两个数据的位置 |\n| rotate | 滚动 |\n| synchronizedList | 线程安全化 |\n\n创建一个集合numbers\n\n```java\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n```\n\n```java\nList numbers = new ArrayList<>();\n\nfor (int i = 0; i < 10; i++) {\n numbers.add(i);\n}\n```\n\n- reverse:\n\n ```java\n Collections.reverse(numbers);\n ```\n\n- shuffle:\n\n ```java\n Collections.shuffle(numbers);\n ```\n\n- sort:\n\n ```java\n Collections.sort(numbers);\n ```\n\n- swap:\n\n ```java\n Collections.swap(numbers,0, 5); //交换第0个和第5个元素\n ```\n\n- rotate:把List中的数据,**向右**滚动指定单位的长度\n\n ```java\n Collections.rotate(numbers,2);\n ```\n\n- synchronizedList:把非线程安全的List转换为线程安全的List\n\n ```java\n //把非线程安全的List转换为线程安全的List\n List synchronizedNumbers = (List) Collections.synchronizedList(numbers);\n \n ```\n\n \n\n\n\n## 关系与区别\n\n## 其他\n\n","source":"_posts/javaSE/17-集合框架.md","raw":"---\ntitle: 17-集合框架\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 9dc8e4af\ndate: 2021-02-17 11:31:29\ndescription: java的集合(容器)介绍,关于这一内容写了2篇笔记,看完不是很理解可以看另一篇《容器》\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 集合框架\n\n## ArrayList类\n\n### 容器概念\n\n我们已经知道,如果要存放多个对象,可以使用数组,但是数组有局限性,比如:\n\n声明长度是10的数组,不用的数组就浪费了,超过10的个数,又放不下\n\n为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是`ArrayList`,[容器的容量](https://how2j.cn/k/number-string/number-string-stringbuilder/328.html#step724)\"capacity\"会随着对象的增加,自动增长,只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。\n\n使用`ArrayList`前需导入:\n\n```java\nimport java.util.ArrayList;\n```\n\n```java\n//容器类ArrayList,用于存放对象\nArrayList heros = new ArrayList();\nheros.add( new Hero(\"盖伦\"));\nSystem.out.println(heros.size());\n\n//容器的容量\"capacity\"会随着对象的增加,自动增长\n//只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。\nheros.add( new Hero(\"提莫\"));\n```\n\n\n\n### ArrayList常用方法\n\n| 关键字 | 简介 | 示例代码 |\n| :------- | :--------------------------- | :-------------------: |\n| add | 增加 | [示例代码](#add) |\n| contains | 判断是否存在 | [示例代码](#contains) |\n| get | 获取指定位置的对象 | [示例代码](#get) |\n| indexOf | 获取对象所处的位置 | [示例代码](#indexOf) |\n| remove | 删除 | [示例代码](#remove) |\n| set | 替换 | [示例代码](#set) |\n| size | 获取大小 | [示例代码](#size) |\n| toArray | 转换为数组 | [示例代码](#toArray) |\n| addAll | 把另一个容器所有对象都加进来 | [示例代码](#addAll) |\n| clear | 清空 | [示例代码](#clear) |\n\n- ##### `add`\n\n ```java\n for (int i = 0; i < 5; i++) {\n \theros.add(new Hero(\"hero \" + i));\n }\n Hero specialHero = new Hero(\"special hero\");\n heros.add(specialHero);\n ```\n\n- ##### `contains`\n\n ```java\n // 初始化5个对象\n System.out.println(heros); //打印heros\n System.out.println(heros.contains(new Hero(\"hero 1\")));\t //false\n System.out.println(heros.contains(specialHero));\t\t//true\n ```\n\n- ##### `get`\n\n 通过get获取指定位置的对象,如果输入的下标越界,一样会报错\n\n ```java\n System.out.println(heros.get(5)); //获取指定位置的对象\n \n System.out.println(heros.get(6)); //如果超出了范围,依然会报错\n ```\n\n- ##### `indexOf`\n\n ```java\n System.out.println(\"specialHero所处的位置:\"+heros.indexOf(specialHero));\n \n System.out.println(\"新英雄,但是名字是\\\"hero 1\\\"所处的位置:\"+heros.indexOf(new Hero(\"hero 1\")));\n ```\n\n- ##### `remove`\n\n - remove可以根据下标删除ArrayList的元素\n\n ```java\n heros.remove(2);\n ```\n\n - 也可以根据对象删除\n\n ```java\n heros.remove(specialHero);\n ```\n\n- ##### `set`\n\n **set**用于替换指定位置的元素\n\n ```java\n heros.set(5, new Hero(\"hero 5\"));\n ```\n\n- ##### `size`\n\n **size** 用于获取ArrayList的大小\n\n ```java\n System.out.println(heros.size());\n ```\n\n- ##### `toArray`\n\n **toArray**可以把一个ArrayList对象转换为数组。\n 需要注意的是,如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组\n\n ```java\n Hero hs[] = (Hero[])heros.toArray(new Hero[]{});\n //传递一个Hero数组类型的对象new Hero[]{}给toArray()\n ```\n\n- ##### `addAll`\n\n **addAll** 把另一个容器所有对象都加进来\n\n ```java\n ArrayList anotherHeros = new ArrayList();\n anotherHeros.add(new Hero(\"hero a\"));\n anotherHeros.add(new Hero(\"hero b\"));\n \n heros.addAll(anotherHeros);\n ```\n\n- ##### `clear`\n\n **clear** 清空一个ArrayList\n\n ```java\n heros.clear();\n System.out.println(\"ArrayList heros:\\t\" + heros);\n ```\n\n\n\n### List接口\n\n- ArrayList与List\n\n ArrayList实现了接口List,常见的写法会把引用声明为接口List类型\n\n > 注意:是**java.util.List**,而**不是**java.awt.List\n\n ```java\n package collection;\n \n import java.util.ArrayList;\n import java.util.List;\n \n import charactor.Hero;\n \n public class TestCollection {\n \n public static void main(String[] args) {\n //ArrayList实现了接口List\n \n //常见的写法会把引用声明为接口List类型\n //注意:是java.util.List,而不是java.awt.List\n //接口引用指向子类对象(多态)\n \n List heros = new ArrayList();\n heros.add( new Hero(\"盖伦\"));\n System.out.println(heros.size());\n \n }\n \n }\n ```\n\n \n\n- List的接口与方法\n\n 因为ArrayList实现了List接口,所以List接口的方法ArrayList都实现了。\n 在【[ArrayList常用方法](#ArrayList常用方法)】有详细的讲解,在此不作赘述\n\n\n\n### 泛型\n\n- 不指定泛型的容器,可以存放任何类型的元素\n- 指定了泛型的容器,只能存放指定类型的元素以及其子类\n\n语法:\n\n```java\nList genericheros = new ArrayList(); //只能存放Hero类型\n\n//简写\nList genericheros = new ArrayList<>();\n```\n\n\n\n### 遍历\n\n| 关键字 | 简介 | 示例代码 |\n| :-------- | :-------------- | :--------------------- |\n| for | 用for循环遍历 | [示例代码](#for) |\n| iterator | 迭代器遍历 | [示例代码](#iterator) |\n| 增强型for | 用增强型for循环 | [示例代码](#增强型for) |\n\n- ##### for\n\n ```java\n for (int i = 0; i < heros.size(); i++) {\n \tHero h = heros.get(i);\n \tSystem.out.println(h);\n }\n ```\n\n- ##### iterator\n\n Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下:\n\n ```java\n import java.util.Iterator; // 引入 Iterator 类\n ```\n\n 迭代器的两个基本操作是 next 、hasNext 和 remove。\n\n - 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。\n\n - 调用 it.hasNext() 用于检测集合中是否还有元素。\n\n - 调用 it.remove() 将迭代器返回的元素删除。\n\n 首先将集合转为迭代器\n\n ```java\n Iterator it= heros.iterator();\n ```\n\n 用while遍历\n\n ```java\n //从最开始的位置判断\"下一个\"位置是否有数据\n while(it.hasNext()){ \t\t//判断是否为null\n \n Hero h = it.next(); \t//通过next取出来,并且把指针向下移动\n System.out.println(h);\n }\n ```\n\n 用for遍历\n\n ```java\n for (Iterator iterator = heros.iterator(); iterator.hasNext();) {\n Hero hero = (Hero) iterator.next();\n System.out.println(hero);\n }\n ```\n\n- ##### 增强型for\n\n ```java\n for (Hero h : heros) {\n \tSystem.out.println(h);\n }\n ```\n\n\n\n## 其他集合\n\n### 链表-LinkList\n\n以下情况使用 ArrayList :\n\n- 频繁访问列表中的某一个元素。\n- 只需要在列表末尾进行添加和删除元素操作。\n\n以下情况使用 LinkedList :\n\n- 你需要通过循环迭代来访问列表中的某些元素。\n- 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。\n\n```java\n//创建链表\nimport java.util.LinkList;\n\nLinkedList list = new LinkedList(); // 普通创建方法\n//或者\nLinkedList list = new LinkedList(Collection c); // 使用集合创建链表\n```\n\n常用方法:\n\n插入:\n\n- addLast()\n- addFirst()\n\n查看:\n\n- getFirst()\n- getLast()\n\n删除:\n\n- removeFirst()\n- removeLast()\n\n\n\n### 队列-Queue\n\nQueue是先进先出队列 FIFO,常用方法:\n\n- `offer(e)` 在最后添加元素e\n- `poll()` 取出第一个元素\n- `peek()` 查看第一个元素\n\n\n\n### 二叉树\n\n二叉树由各种**节点**组成\n\n二叉树特点:\n\n- 每个节点都可以有**左子**节点,**右子**节点\t\n- 每一个节点都有一个**值**\n\n\"image-20210212204447345\"\n\n\n\n```java\npackage collection;\n \npublic class Node {\n public Node leftNode;\t// 左子节点\n \n public Node rightNode;\t// 右子节点\n \n public Object value;\t// 值\n}\n```\n\n### HashMap\n\n1. HashMap 是一个散列表,它存储的内容是键值对`key-value`映射;\n\n2. HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步;\n\n3. HashMap 是**无序**的,即不会记录插入的顺序;\n4. HashMap中的key不能重复,value可以重复;\n5. HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。\n\n\n\"image-20210212205720886\"\n\n创建一个 HashMap 对象 Sites,其中:\n\n- key为整型\n\n- value为字符串(String)类型\n\n ```java\n import java.util.HashMap; // 引入 HashMap 类\n \n HashMap Sites = new HashMap();\n ```\n\n> 添加元素 `put(key, value)`\n>\n> ```java\n> import java.util.HashMap;\n> \n> public class HashMapTest {\n> public static void main(String[] args) {\n> // 创建 HashMap 对象 Sites\n> HashMap Sites = new HashMap();\n> // 添加键值对\n> Sites.put(1, \"Google\");\n> Sites.put(2, \"Runoob\");\n> Sites.put(3, \"Taobao\");\n> Sites.put(4, \"Zhihu\");\n> System.out.println(Sites);\n> }\n> }\n> ```\n\n> 访问元素的value值:`get(key)`\n>\n> ```java\n> System.out.println(Sites.get(3));\n> ```\n\n> 删除元素(键值对):`remove(key)`\n>\n> ```java\n> Sites.remove(4);\n> ```\n> 清空所有键值对:`clear()`\n>\n> ```java\n> Sites.clear();\n> ```\n\n---\n\n> 返回元素(键值对)数量:`size()`\n>\n> ```java\n> Sites.size();\n> ```\n\n---\n\n> 返回所有key:`keySet()`\n>\n> ```java\n> Sites.keySet()\n> ```\n>\n> 返回所有values:`values()`\n>\n> ```java\n> Sites.values()\n> ```\n\n### HashSet\n\n1. HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。\n2. HashSet 允许有 null 值。\n3. HashSet 是无序的,即不会记录插入的顺序。\n4. 由于Set是无序的,所以Set不可以获取指定位置的元素。\n5. HashSet 实现了 Set 接口。\n\n\"image-20210212212148854\"\n\n创建一个Set:\n\n```java\nHashSet sites = new HashSet();\n```\n\n- 添加元素:`sites.add(e)`\n- 判断元素e是否存在:`sites.contains(e)`\n- 删除元素:`sites.remove(e)`\n- 清空元素:`sites.clear()`\n- 计算大小:`sites.size()`\n\n### Collection\n\nCollection是 Set、List、Queue、Deque的接口(Deque 继承 Queue,间接的继承了 Collection)\n\n- Queue: 先进先出队列\n- Deque: 双向链表\n\n> 注:Collection 和 Map 之间没有关系,Collection是放一个一个对象的,Map 是放键值对的\n\n### Collections\n\nCollections是一个类,**容器**的工具类,就如同Arrays是数组的工具类\n\nCollections具有以下方法:\n\n| 关键字 | 简介 |\n| :--------------- | :----------------- |\n| reverse | 反转 |\n| shuffle | 混淆 |\n| sort | 排序 |\n| swap | 交换两个数据的位置 |\n| rotate | 滚动 |\n| synchronizedList | 线程安全化 |\n\n创建一个集合numbers\n\n```java\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n```\n\n```java\nList numbers = new ArrayList<>();\n\nfor (int i = 0; i < 10; i++) {\n numbers.add(i);\n}\n```\n\n- reverse:\n\n ```java\n Collections.reverse(numbers);\n ```\n\n- shuffle:\n\n ```java\n Collections.shuffle(numbers);\n ```\n\n- sort:\n\n ```java\n Collections.sort(numbers);\n ```\n\n- swap:\n\n ```java\n Collections.swap(numbers,0, 5); //交换第0个和第5个元素\n ```\n\n- rotate:把List中的数据,**向右**滚动指定单位的长度\n\n ```java\n Collections.rotate(numbers,2);\n ```\n\n- synchronizedList:把非线程安全的List转换为线程安全的List\n\n ```java\n //把非线程安全的List转换为线程安全的List\n List synchronizedNumbers = (List) Collections.synchronizedList(numbers);\n \n ```\n\n \n\n\n\n## 关系与区别\n\n## 其他\n\n","slug":"javaSE/17-集合框架","published":1,"updated":"2021-08-29T15:31:45.529Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qt006aakvealgt52ey","content":"

集合框架

ArrayList类

容器概念

我们已经知道,如果要存放多个对象,可以使用数组,但是数组有局限性,比如:

\n

声明长度是10的数组,不用的数组就浪费了,超过10的个数,又放不下

\n

为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是ArrayList容器的容量“capacity”会随着对象的增加,自动增长,只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。

\n

使用ArrayList前需导入:

\n
1
import java.util.ArrayList;
\n
1
2
3
4
5
6
7
8
//容器类ArrayList,用于存放对象
ArrayList heros = new ArrayList();
heros.add( new Hero("盖伦"));
System.out.println(heros.size());

//容器的容量"capacity"会随着对象的增加,自动增长
//只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。
heros.add( new Hero("提莫"));
\n

ArrayList常用方法

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介示例代码
add增加示例代码
contains判断是否存在示例代码
get获取指定位置的对象示例代码
indexOf获取对象所处的位置示例代码
remove删除示例代码
set替换示例代码
size获取大小示例代码
toArray转换为数组示例代码
addAll把另一个容器所有对象都加进来示例代码
clear清空示例代码
\n
\n
    \n
  • add
    1
    2
    3
    4
    5
    for (int i = 0; i < 5; i++) {
    \theros.add(new Hero("hero " + i));
    }
    Hero specialHero = new Hero("special hero");
    heros.add(specialHero);
    \n
  • \n
  • contains
    1
    2
    3
    4
    // 初始化5个对象
    System.out.println(heros); //打印heros
    System.out.println(heros.contains(new Hero("hero 1")));\t //false
    System.out.println(heros.contains(specialHero));\t\t//true
    \n
  • \n
  • get

    通过get获取指定位置的对象,如果输入的下标越界,一样会报错

    \n
    1
    2
    3
    System.out.println(heros.get(5)); //获取指定位置的对象

    System.out.println(heros.get(6)); //如果超出了范围,依然会报错
    \n
  • \n
  • indexOf
    1
    2
    3
    System.out.println("specialHero所处的位置:"+heros.indexOf(specialHero));

    System.out.println("新英雄,但是名字是\\"hero 1\\"所处的位置:"+heros.indexOf(new Hero("hero 1")));
    \n
  • \n
  • remove
      \n
    • remove可以根据下标删除ArrayList的元素

      \n
      1
      heros.remove(2);
      \n
    • \n
    • 也可以根据对象删除

      \n
      1
      heros.remove(specialHero);
      \n
    • \n
    \n
  • \n
  • set

    set用于替换指定位置的元素

    \n
    1
    heros.set(5, new Hero("hero 5"));
    \n
  • \n
  • size

    size 用于获取ArrayList的大小

    \n
    1
    System.out.println(heros.size());
    \n
  • \n
  • toArray

    toArray可以把一个ArrayList对象转换为数组。
    需要注意的是,如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组

    \n
    1
    2
    Hero hs[] = (Hero[])heros.toArray(new Hero[]{});
    //传递一个Hero数组类型的对象new Hero[]{}给toArray()
    \n
  • \n
  • addAll

    addAll 把另一个容器所有对象都加进来

    \n
    1
    2
    3
    4
    5
    ArrayList anotherHeros = new ArrayList();
    anotherHeros.add(new Hero("hero a"));
    anotherHeros.add(new Hero("hero b"));

    heros.addAll(anotherHeros);
    \n
  • \n
  • clear

    clear 清空一个ArrayList

    \n
    1
    2
    heros.clear();
    System.out.println("ArrayList heros:\\t" + heros);
    \n
  • \n
\n

List接口

    \n
  • ArrayList与List

    \n

    ArrayList实现了接口List,常见的写法会把引用声明为接口List类型

    \n
    \n

    注意:是java.util.List,而不是java.awt.List

    \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package collection;

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

    import charactor.Hero;

    public class TestCollection {

    public static void main(String[] args) {
    //ArrayList实现了接口List

    //常见的写法会把引用声明为接口List类型
    //注意:是java.util.List,而不是java.awt.List
    //接口引用指向子类对象(多态)

    List heros = new ArrayList();
    heros.add( new Hero("盖伦"));
    System.out.println(heros.size());

    }

    }
    \n
  • \n
\n
    \n
  • List的接口与方法

    \n

    因为ArrayList实现了List接口,所以List接口的方法ArrayList都实现了。
    在【ArrayList常用方法】有详细的讲解,在此不作赘述

    \n
  • \n
\n

泛型

    \n
  • 不指定泛型的容器,可以存放任何类型的元素
  • \n
  • 指定了泛型的容器,只能存放指定类型的元素以及其子类
  • \n
\n

语法:

\n
1
2
3
4
List<Hero> genericheros = new ArrayList<Hero>(); //只能存放Hero类型

//简写
List<Hero> genericheros = new ArrayList<>();
\n

遍历

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介示例代码
for用for循环遍历示例代码
iterator迭代器遍历示例代码
增强型for用增强型for循环示例代码
\n
\n
    \n
  • for
    1
    2
    3
    4
    for (int i = 0; i < heros.size(); i++) {
    \tHero h = heros.get(i);
    \tSystem.out.println(h);
    }
    \n
  • \n
  • iterator

    Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下:

    \n
    1
    import java.util.Iterator; // 引入 Iterator 类
    \n

    迭代器的两个基本操作是 next 、hasNext 和 remove。

    \n
      \n
    • 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。

      \n
    • \n
    • 调用 it.hasNext() 用于检测集合中是否还有元素。

      \n
    • \n
    • 调用 it.remove() 将迭代器返回的元素删除。

      \n
    • \n
    \n

    首先将集合转为迭代器

    \n
    1
    Iterator<Hero> it= heros.iterator();
    \n

    用while遍历

    \n
    1
    2
    3
    4
    5
    6
    //从最开始的位置判断"下一个"位置是否有数据
    while(it.hasNext()){ \t\t//判断是否为null

    Hero h = it.next(); \t//通过next取出来,并且把指针向下移动
    System.out.println(h);
    }
    \n

    用for遍历

    \n
    1
    2
    3
    4
    for (Iterator<Hero> iterator = heros.iterator(); iterator.hasNext();) {
    Hero hero = (Hero) iterator.next();
    System.out.println(hero);
    }
    \n
  • \n
  • 增强型for
    1
    2
    3
    for (Hero h : heros) {
    \tSystem.out.println(h);
    }
    \n
  • \n
\n

其他集合

链表-LinkList

以下情况使用 ArrayList :

\n
    \n
  • 频繁访问列表中的某一个元素。
  • \n
  • 只需要在列表末尾进行添加和删除元素操作。
  • \n
\n

以下情况使用 LinkedList :

\n
    \n
  • 你需要通过循环迭代来访问列表中的某些元素。
  • \n
  • 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
  • \n
\n
1
2
3
4
5
6
//创建链表
import java.util.LinkList;

LinkedList<E> list = new LinkedList<E>(); // 普通创建方法
//或者
LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表
\n

常用方法:

\n

插入:

\n
    \n
  • addLast()
  • \n
  • addFirst()
  • \n
\n

查看:

\n
    \n
  • getFirst()
  • \n
  • getLast()
  • \n
\n

删除:

\n
    \n
  • removeFirst()
  • \n
  • removeLast()
  • \n
\n

队列-Queue

Queue是先进先出队列 FIFO,常用方法:

\n
    \n
  • offer(e) 在最后添加元素e
  • \n
  • poll() 取出第一个元素
  • \n
  • peek() 查看第一个元素
  • \n
\n

二叉树

二叉树由各种节点组成

\n

二叉树特点:

\n
    \n
  • 每个节点都可以有左子节点,右子节点
  • \n
  • 每一个节点都有一个
  • \n
\n

\"image-20210212204447345\"

\n
1
2
3
4
5
6
7
8
9
package collection;

public class Node {
public Node leftNode;\t// 左子节点

public Node rightNode;\t// 右子节点

public Object value;\t// 值
}
\n

HashMap

    \n
  1. HashMap 是一个散列表,它存储的内容是键值对key-value映射;

    \n
  2. \n
  3. HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步;

    \n
  4. \n
  5. HashMap 是无序的,即不会记录插入的顺序;

    \n
  6. \n
  7. HashMap中的key不能重复,value可以重复;
  8. \n
  9. HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。
  10. \n
\n

\"image-20210212205720886\"

\n

创建一个 HashMap 对象 Sites,其中:

\n
    \n
  • key为整型

    \n
  • \n
  • value为字符串(String)类型

    \n
    1
    2
    3
    import java.util.HashMap; // 引入 HashMap 类

    HashMap<Integer, String> Sites = new HashMap<Integer, String>();
    \n
  • \n
\n
\n

添加元素 put(key, value)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.HashMap;

public class HashMapTest {
public static void main(String[] args) {
// 创建 HashMap 对象 Sites
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
// 添加键值对
Sites.put(1, "Google");
Sites.put(2, "Runoob");
Sites.put(3, "Taobao");
Sites.put(4, "Zhihu");
System.out.println(Sites);
}
}
\n

访问元素的value值:get(key)

\n
1
System.out.println(Sites.get(3));
\n

删除元素(键值对):remove(key)

\n
1
Sites.remove(4);
\n

清空所有键值对:clear()

\n
1
Sites.clear();
\n
\n
\n
\n

返回元素(键值对)数量:size()

\n
1
Sites.size();
\n
\n
\n
\n

返回所有key:keySet()

\n
1
Sites.keySet()
\n

返回所有values:values()

\n
1
Sites.values()
\n
\n

HashSet

    \n
  1. HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
  2. \n
  3. HashSet 允许有 null 值。
  4. \n
  5. HashSet 是无序的,即不会记录插入的顺序。
  6. \n
  7. 由于Set是无序的,所以Set不可以获取指定位置的元素。
  8. \n
  9. HashSet 实现了 Set 接口。
  10. \n
\n

\"image-20210212212148854\"

\n

创建一个Set:

\n
1
HashSet<String> sites = new HashSet<String>();
\n
    \n
  • 添加元素:sites.add(e)
  • \n
  • 判断元素e是否存在:sites.contains(e)
  • \n
  • 删除元素:sites.remove(e)
  • \n
  • 清空元素:sites.clear()
  • \n
  • 计算大小:sites.size()
  • \n
\n

Collection

Collection是 Set、List、Queue、Deque的接口(Deque 继承 Queue,间接的继承了 Collection)

\n
    \n
  • Queue: 先进先出队列
  • \n
  • Deque: 双向链表
  • \n
\n
\n

注:Collection 和 Map 之间没有关系,Collection是放一个一个对象的,Map 是放键值对的

\n
\n

Collections

Collections是一个类,容器的工具类,就如同Arrays是数组的工具类

\n

Collections具有以下方法:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介
reverse反转
shuffle混淆
sort排序
swap交换两个数据的位置
rotate滚动
synchronizedList线程安全化
\n
\n

创建一个集合numbers

\n
1
2
3
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
\n
1
2
3
4
5
List<Integer> numbers = new ArrayList<>();

for (int i = 0; i < 10; i++) {
numbers.add(i);
}
\n
    \n
  • reverse:

    \n
    1
    Collections.reverse(numbers);
    \n
  • \n
  • shuffle:

    \n
    1
    Collections.shuffle(numbers);
    \n
  • \n
  • sort:

    \n
    1
    Collections.sort(numbers);
    \n
  • \n
  • swap:

    \n
    1
    Collections.swap(numbers,0, 5);  //交换第0个和第5个元素
    \n
  • \n
  • rotate:把List中的数据,向右滚动指定单位的长度

    \n
    1
    Collections.rotate(numbers,2);
    \n
  • \n
  • synchronizedList:把非线程安全的List转换为线程安全的List

    \n
    1
    2
    3
    //把非线程安全的List转换为线程安全的List
    List<Integer> synchronizedNumbers = (List<Integer>) Collections.synchronizedList(numbers);

    \n
  • \n
\n

关系与区别

其他

","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

集合框架

ArrayList类

容器概念

我们已经知道,如果要存放多个对象,可以使用数组,但是数组有局限性,比如:

\n

声明长度是10的数组,不用的数组就浪费了,超过10的个数,又放不下

\n

为了解决数组的局限性,引入容器类的概念。 最常见的容器类就是ArrayList容器的容量“capacity”会随着对象的增加,自动增长,只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。

\n

使用ArrayList前需导入:

\n
1
import java.util.ArrayList;
\n
1
2
3
4
5
6
7
8
//容器类ArrayList,用于存放对象
ArrayList heros = new ArrayList();
heros.add( new Hero("盖伦"));
System.out.println(heros.size());

//容器的容量"capacity"会随着对象的增加,自动增长
//只需要不断往容器里增加英雄即可,不用担心会出现数组的边界问题。
heros.add( new Hero("提莫"));
\n

ArrayList常用方法

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介示例代码
add增加示例代码
contains判断是否存在示例代码
get获取指定位置的对象示例代码
indexOf获取对象所处的位置示例代码
remove删除示例代码
set替换示例代码
size获取大小示例代码
toArray转换为数组示例代码
addAll把另一个容器所有对象都加进来示例代码
clear清空示例代码
\n
\n
    \n
  • add
    1
    2
    3
    4
    5
    for (int i = 0; i < 5; i++) {
    \theros.add(new Hero("hero " + i));
    }
    Hero specialHero = new Hero("special hero");
    heros.add(specialHero);
    \n
  • \n
  • contains
    1
    2
    3
    4
    // 初始化5个对象
    System.out.println(heros); //打印heros
    System.out.println(heros.contains(new Hero("hero 1")));\t //false
    System.out.println(heros.contains(specialHero));\t\t//true
    \n
  • \n
  • get

    通过get获取指定位置的对象,如果输入的下标越界,一样会报错

    \n
    1
    2
    3
    System.out.println(heros.get(5)); //获取指定位置的对象

    System.out.println(heros.get(6)); //如果超出了范围,依然会报错
    \n
  • \n
  • indexOf
    1
    2
    3
    System.out.println("specialHero所处的位置:"+heros.indexOf(specialHero));

    System.out.println("新英雄,但是名字是\\"hero 1\\"所处的位置:"+heros.indexOf(new Hero("hero 1")));
    \n
  • \n
  • remove
      \n
    • remove可以根据下标删除ArrayList的元素

      \n
      1
      heros.remove(2);
      \n
    • \n
    • 也可以根据对象删除

      \n
      1
      heros.remove(specialHero);
      \n
    • \n
    \n
  • \n
  • set

    set用于替换指定位置的元素

    \n
    1
    heros.set(5, new Hero("hero 5"));
    \n
  • \n
  • size

    size 用于获取ArrayList的大小

    \n
    1
    System.out.println(heros.size());
    \n
  • \n
  • toArray

    toArray可以把一个ArrayList对象转换为数组。
    需要注意的是,如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组

    \n
    1
    2
    Hero hs[] = (Hero[])heros.toArray(new Hero[]{});
    //传递一个Hero数组类型的对象new Hero[]{}给toArray()
    \n
  • \n
  • addAll

    addAll 把另一个容器所有对象都加进来

    \n
    1
    2
    3
    4
    5
    ArrayList anotherHeros = new ArrayList();
    anotherHeros.add(new Hero("hero a"));
    anotherHeros.add(new Hero("hero b"));

    heros.addAll(anotherHeros);
    \n
  • \n
  • clear

    clear 清空一个ArrayList

    \n
    1
    2
    heros.clear();
    System.out.println("ArrayList heros:\\t" + heros);
    \n
  • \n
\n

List接口

    \n
  • ArrayList与List

    \n

    ArrayList实现了接口List,常见的写法会把引用声明为接口List类型

    \n
    \n

    注意:是java.util.List,而不是java.awt.List

    \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package collection;

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

    import charactor.Hero;

    public class TestCollection {

    public static void main(String[] args) {
    //ArrayList实现了接口List

    //常见的写法会把引用声明为接口List类型
    //注意:是java.util.List,而不是java.awt.List
    //接口引用指向子类对象(多态)

    List heros = new ArrayList();
    heros.add( new Hero("盖伦"));
    System.out.println(heros.size());

    }

    }
    \n
  • \n
\n
    \n
  • List的接口与方法

    \n

    因为ArrayList实现了List接口,所以List接口的方法ArrayList都实现了。
    在【ArrayList常用方法】有详细的讲解,在此不作赘述

    \n
  • \n
\n

泛型

    \n
  • 不指定泛型的容器,可以存放任何类型的元素
  • \n
  • 指定了泛型的容器,只能存放指定类型的元素以及其子类
  • \n
\n

语法:

\n
1
2
3
4
List<Hero> genericheros = new ArrayList<Hero>(); //只能存放Hero类型

//简写
List<Hero> genericheros = new ArrayList<>();
\n

遍历

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介示例代码
for用for循环遍历示例代码
iterator迭代器遍历示例代码
增强型for用增强型for循环示例代码
\n
\n
    \n
  • for
    1
    2
    3
    4
    for (int i = 0; i < heros.size(); i++) {
    \tHero h = heros.get(i);
    \tSystem.out.println(h);
    }
    \n
  • \n
  • iterator

    Iterator 类位于 java.util 包中,使用前需要引入它,语法格式如下:

    \n
    1
    import java.util.Iterator; // 引入 Iterator 类
    \n

    迭代器的两个基本操作是 next 、hasNext 和 remove。

    \n
      \n
    • 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。

      \n
    • \n
    • 调用 it.hasNext() 用于检测集合中是否还有元素。

      \n
    • \n
    • 调用 it.remove() 将迭代器返回的元素删除。

      \n
    • \n
    \n

    首先将集合转为迭代器

    \n
    1
    Iterator<Hero> it= heros.iterator();
    \n

    用while遍历

    \n
    1
    2
    3
    4
    5
    6
    //从最开始的位置判断"下一个"位置是否有数据
    while(it.hasNext()){ \t\t//判断是否为null

    Hero h = it.next(); \t//通过next取出来,并且把指针向下移动
    System.out.println(h);
    }
    \n

    用for遍历

    \n
    1
    2
    3
    4
    for (Iterator<Hero> iterator = heros.iterator(); iterator.hasNext();) {
    Hero hero = (Hero) iterator.next();
    System.out.println(hero);
    }
    \n
  • \n
  • 增强型for
    1
    2
    3
    for (Hero h : heros) {
    \tSystem.out.println(h);
    }
    \n
  • \n
\n

其他集合

链表-LinkList

以下情况使用 ArrayList :

\n
    \n
  • 频繁访问列表中的某一个元素。
  • \n
  • 只需要在列表末尾进行添加和删除元素操作。
  • \n
\n

以下情况使用 LinkedList :

\n
    \n
  • 你需要通过循环迭代来访问列表中的某些元素。
  • \n
  • 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
  • \n
\n
1
2
3
4
5
6
//创建链表
import java.util.LinkList;

LinkedList<E> list = new LinkedList<E>(); // 普通创建方法
//或者
LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表
\n

常用方法:

\n

插入:

\n
    \n
  • addLast()
  • \n
  • addFirst()
  • \n
\n

查看:

\n
    \n
  • getFirst()
  • \n
  • getLast()
  • \n
\n

删除:

\n
    \n
  • removeFirst()
  • \n
  • removeLast()
  • \n
\n

队列-Queue

Queue是先进先出队列 FIFO,常用方法:

\n
    \n
  • offer(e) 在最后添加元素e
  • \n
  • poll() 取出第一个元素
  • \n
  • peek() 查看第一个元素
  • \n
\n

二叉树

二叉树由各种节点组成

\n

二叉树特点:

\n
    \n
  • 每个节点都可以有左子节点,右子节点
  • \n
  • 每一个节点都有一个
  • \n
\n

\"image-20210212204447345\"

\n
1
2
3
4
5
6
7
8
9
package collection;

public class Node {
public Node leftNode;\t// 左子节点

public Node rightNode;\t// 右子节点

public Object value;\t// 值
}
\n

HashMap

    \n
  1. HashMap 是一个散列表,它存储的内容是键值对key-value映射;

    \n
  2. \n
  3. HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步;

    \n
  4. \n
  5. HashMap 是无序的,即不会记录插入的顺序;

    \n
  6. \n
  7. HashMap中的key不能重复,value可以重复;
  8. \n
  9. HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。
  10. \n
\n

\"image-20210212205720886\"

\n

创建一个 HashMap 对象 Sites,其中:

\n
    \n
  • key为整型

    \n
  • \n
  • value为字符串(String)类型

    \n
    1
    2
    3
    import java.util.HashMap; // 引入 HashMap 类

    HashMap<Integer, String> Sites = new HashMap<Integer, String>();
    \n
  • \n
\n
\n

添加元素 put(key, value)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.HashMap;

public class HashMapTest {
public static void main(String[] args) {
// 创建 HashMap 对象 Sites
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
// 添加键值对
Sites.put(1, "Google");
Sites.put(2, "Runoob");
Sites.put(3, "Taobao");
Sites.put(4, "Zhihu");
System.out.println(Sites);
}
}
\n

访问元素的value值:get(key)

\n
1
System.out.println(Sites.get(3));
\n

删除元素(键值对):remove(key)

\n
1
Sites.remove(4);
\n

清空所有键值对:clear()

\n
1
Sites.clear();
\n
\n
\n
\n

返回元素(键值对)数量:size()

\n
1
Sites.size();
\n
\n
\n
\n

返回所有key:keySet()

\n
1
Sites.keySet()
\n

返回所有values:values()

\n
1
Sites.values()
\n
\n

HashSet

    \n
  1. HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
  2. \n
  3. HashSet 允许有 null 值。
  4. \n
  5. HashSet 是无序的,即不会记录插入的顺序。
  6. \n
  7. 由于Set是无序的,所以Set不可以获取指定位置的元素。
  8. \n
  9. HashSet 实现了 Set 接口。
  10. \n
\n

\"image-20210212212148854\"

\n

创建一个Set:

\n
1
HashSet<String> sites = new HashSet<String>();
\n
    \n
  • 添加元素:sites.add(e)
  • \n
  • 判断元素e是否存在:sites.contains(e)
  • \n
  • 删除元素:sites.remove(e)
  • \n
  • 清空元素:sites.clear()
  • \n
  • 计算大小:sites.size()
  • \n
\n

Collection

Collection是 Set、List、Queue、Deque的接口(Deque 继承 Queue,间接的继承了 Collection)

\n
    \n
  • Queue: 先进先出队列
  • \n
  • Deque: 双向链表
  • \n
\n
\n

注:Collection 和 Map 之间没有关系,Collection是放一个一个对象的,Map 是放键值对的

\n
\n

Collections

Collections是一个类,容器的工具类,就如同Arrays是数组的工具类

\n

Collections具有以下方法:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
关键字简介
reverse反转
shuffle混淆
sort排序
swap交换两个数据的位置
rotate滚动
synchronizedList线程安全化
\n
\n

创建一个集合numbers

\n
1
2
3
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
\n
1
2
3
4
5
List<Integer> numbers = new ArrayList<>();

for (int i = 0; i < 10; i++) {
numbers.add(i);
}
\n
    \n
  • reverse:

    \n
    1
    Collections.reverse(numbers);
    \n
  • \n
  • shuffle:

    \n
    1
    Collections.shuffle(numbers);
    \n
  • \n
  • sort:

    \n
    1
    Collections.sort(numbers);
    \n
  • \n
  • swap:

    \n
    1
    Collections.swap(numbers,0, 5);  //交换第0个和第5个元素
    \n
  • \n
  • rotate:把List中的数据,向右滚动指定单位的长度

    \n
    1
    Collections.rotate(numbers,2);
    \n
  • \n
  • synchronizedList:把非线程安全的List转换为线程安全的List

    \n
    1
    2
    3
    //把非线程安全的List转换为线程安全的List
    List<Integer> synchronizedNumbers = (List<Integer>) Collections.synchronizedList(numbers);

    \n
  • \n
\n

关系与区别

其他

"},{"title":"18-泛型","abbrlink":"e0bbd322","date":"2021-02-18T02:32:47.000Z","description":"java泛型介绍,使用、自定义泛型、通配符","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 泛型\n\n## 泛型用法\n\n泛型的用法是在容器后面添加\\\n\n```java\nArrayList heros = new ArrayList();\n\n//简写:\nArrayList heros2 = new ArrayList<>();\n```\n\nType可以是类,抽象类,接口;\n\n泛型表示这种容器,只能存放APHero,ADHero就放不进去了。\n\n## 创建支持泛型的类\n\n1. 设计一个支持泛型的栈 MyStack\n2. 设计这个类的时候,在类的声明上,加上一个,表示该类支持泛型。\n3. T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。\n\n```java\npackage generic;\n \nimport java.util.HashMap;\nimport java.util.LinkedList;\n \nimport charactor.Hero;\nimport property.Item;\n \npublic class MyStack {\n LinkedList values = new LinkedList();\n \n public void push(T t) {\n values.addLast(t);\n }\n \n public T pull() {\n return values.removeLast();\n }\n \n public T peek() {\n return values.getLast();\n }\n \n public static void main(String[] args) {\n //在声明这个Stack的时候,使用泛型就表示该Stack只能放Hero\n MyStack heroStack = new MyStack<>();\n heroStack.push(new Hero());\n //不能放Item\n heroStack.push(new Item());\n \n //在声明这个Stack的时候,使用泛型就表示该Stack只能放Item\n MyStack itemStack = new MyStack<>();\n itemStack.push(new Item());\n //不能放Hero\n itemStack.push(new Hero());\n }\n \n}\n```\n\n\n\n## 通配符\n\n### ? extends\n\n`ArrayList heroList` 表示这是一个Hero泛型或者其子类泛型(可以理解为这个heroList的元素类型是:Hero或其子类类型):\n\n- heroList 的泛型可能是Hero\n- heroList 的泛型可能是APHero\n- heroList 的泛型可能是ADHero\n\n所以 可以确定的是,从heroList取出来的对象,一定是可以转型成Hero的\n\n```java\nArrayList apHeroList = new ArrayList();\napHeroList.add(new APHero());\n\nArrayList heroList = apHeroList;\n\n//可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的\nHero h = heroList.get(0);\n```\n\n\n\n> 注:不能往里面**放东西**,因为:\n>\n> - 放APHero就不满足\\\n> - 放ADHero又不满足\\\n>\n> ```java\n> heroList.add(new ADHero()); //编译错误,因为heroList的泛型有可能是APHero\n> ```\n\n\n\n### ? super\n\n`ArrayList heroList` 表示这是一个Hero或者其父类泛型:\n\n- heroList的泛型可能是Hero\n- heroList的泛型可能是Object\n\n可以往里面插入Hero以及Hero的子类:\n\n- 放Hero没问题\n- 放APHero、ADHero也没问题\n\n> 但是**取出来有风险**,因为不确定取出来是Hero还是Object\n\n\"?\n\n\n\n```java\nArrayList heroList = new ArrayList();\n//? super Hero 表示 heroList的泛型是Hero或者其父类泛型Object\n```\n\n```java\n//所以就可以插入Hero\nheroList.add(new Hero());\n\n//也可以插入Hero的子类\nheroList.add(new APHero());\nheroList.add(new ADHero());\n```\n\n```java\n//但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Hero会失败\nHero h= heroList.get(0);\n```\n\n\n\n### 泛型通配符【?】\n\n泛型通配符`?`代表任意泛型\n既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能;\n\n- 取:只能以Object的形式取出来\n- 放:不能往里面放对象,因为不知道到底是一个什么泛型的容器\n\n```java\nArrayList apHeroList = new ArrayList();\n\n//?泛型通配符,表示任意泛型\nArrayList generalList = apHeroList;\n\n//?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型\n//所以只能以Object的形式取出来\nObject o = generalList.get(0);\n\n//?的缺陷2: 既然?代表任意泛型,那么既有可能是Hero,也有可能是Item\n//所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去\ngeneralList.add(new Item()); //编译错误 因为?代表任意泛型,很有可能不是Item\ngeneralList.add(new Hero()); //编译错误 因为?代表任意泛型,很有可能不是Hero\ngeneralList.add(new APHero()); //编译错误 因为?代表任意泛型,很有可能不是APHero\n```\n\n\n\n### 总结\n\n如果希望只取出,不插入,就使用`? extends Hero`\n\n如果希望只插入,不取出,就使用`? super Hero`\n\n如果希望,又能插入,又能取出,就**不要**用通配符`?`\n\n\n\n## 泛型转型\n\n### 对象转型\n\n根据面向对象学习的知识,==子类转父类== 是一定可以成功的\n\n\n\n### 子类泛型转父类泛型\n\n既然子类对象转父类对象是可以成功的,那么子类泛型转父类泛型能成功吗?(不能)\n例如:\n\n- hs的泛型是父类Hero\n- adhs 的泛型是子类ADHero\n\n那么 把adhs转换为hs能成功吗?(不能)\n\n> 父类转子类也不能\n\n\n\n","source":"_posts/javaSE/18-泛型.md","raw":"---\ntitle: 18-泛型\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: e0bbd322\ndate: 2021-02-18 10:32:47\ndescription: java泛型介绍,使用、自定义泛型、通配符\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 泛型\n\n## 泛型用法\n\n泛型的用法是在容器后面添加\\\n\n```java\nArrayList heros = new ArrayList();\n\n//简写:\nArrayList heros2 = new ArrayList<>();\n```\n\nType可以是类,抽象类,接口;\n\n泛型表示这种容器,只能存放APHero,ADHero就放不进去了。\n\n## 创建支持泛型的类\n\n1. 设计一个支持泛型的栈 MyStack\n2. 设计这个类的时候,在类的声明上,加上一个,表示该类支持泛型。\n3. T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。\n\n```java\npackage generic;\n \nimport java.util.HashMap;\nimport java.util.LinkedList;\n \nimport charactor.Hero;\nimport property.Item;\n \npublic class MyStack {\n LinkedList values = new LinkedList();\n \n public void push(T t) {\n values.addLast(t);\n }\n \n public T pull() {\n return values.removeLast();\n }\n \n public T peek() {\n return values.getLast();\n }\n \n public static void main(String[] args) {\n //在声明这个Stack的时候,使用泛型就表示该Stack只能放Hero\n MyStack heroStack = new MyStack<>();\n heroStack.push(new Hero());\n //不能放Item\n heroStack.push(new Item());\n \n //在声明这个Stack的时候,使用泛型就表示该Stack只能放Item\n MyStack itemStack = new MyStack<>();\n itemStack.push(new Item());\n //不能放Hero\n itemStack.push(new Hero());\n }\n \n}\n```\n\n\n\n## 通配符\n\n### ? extends\n\n`ArrayList heroList` 表示这是一个Hero泛型或者其子类泛型(可以理解为这个heroList的元素类型是:Hero或其子类类型):\n\n- heroList 的泛型可能是Hero\n- heroList 的泛型可能是APHero\n- heroList 的泛型可能是ADHero\n\n所以 可以确定的是,从heroList取出来的对象,一定是可以转型成Hero的\n\n```java\nArrayList apHeroList = new ArrayList();\napHeroList.add(new APHero());\n\nArrayList heroList = apHeroList;\n\n//可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的\nHero h = heroList.get(0);\n```\n\n\n\n> 注:不能往里面**放东西**,因为:\n>\n> - 放APHero就不满足\\\n> - 放ADHero又不满足\\\n>\n> ```java\n> heroList.add(new ADHero()); //编译错误,因为heroList的泛型有可能是APHero\n> ```\n\n\n\n### ? super\n\n`ArrayList heroList` 表示这是一个Hero或者其父类泛型:\n\n- heroList的泛型可能是Hero\n- heroList的泛型可能是Object\n\n可以往里面插入Hero以及Hero的子类:\n\n- 放Hero没问题\n- 放APHero、ADHero也没问题\n\n> 但是**取出来有风险**,因为不确定取出来是Hero还是Object\n\n\"?\n\n\n\n```java\nArrayList heroList = new ArrayList();\n//? super Hero 表示 heroList的泛型是Hero或者其父类泛型Object\n```\n\n```java\n//所以就可以插入Hero\nheroList.add(new Hero());\n\n//也可以插入Hero的子类\nheroList.add(new APHero());\nheroList.add(new ADHero());\n```\n\n```java\n//但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Hero会失败\nHero h= heroList.get(0);\n```\n\n\n\n### 泛型通配符【?】\n\n泛型通配符`?`代表任意泛型\n既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能;\n\n- 取:只能以Object的形式取出来\n- 放:不能往里面放对象,因为不知道到底是一个什么泛型的容器\n\n```java\nArrayList apHeroList = new ArrayList();\n\n//?泛型通配符,表示任意泛型\nArrayList generalList = apHeroList;\n\n//?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型\n//所以只能以Object的形式取出来\nObject o = generalList.get(0);\n\n//?的缺陷2: 既然?代表任意泛型,那么既有可能是Hero,也有可能是Item\n//所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去\ngeneralList.add(new Item()); //编译错误 因为?代表任意泛型,很有可能不是Item\ngeneralList.add(new Hero()); //编译错误 因为?代表任意泛型,很有可能不是Hero\ngeneralList.add(new APHero()); //编译错误 因为?代表任意泛型,很有可能不是APHero\n```\n\n\n\n### 总结\n\n如果希望只取出,不插入,就使用`? extends Hero`\n\n如果希望只插入,不取出,就使用`? super Hero`\n\n如果希望,又能插入,又能取出,就**不要**用通配符`?`\n\n\n\n## 泛型转型\n\n### 对象转型\n\n根据面向对象学习的知识,==子类转父类== 是一定可以成功的\n\n\n\n### 子类泛型转父类泛型\n\n既然子类对象转父类对象是可以成功的,那么子类泛型转父类泛型能成功吗?(不能)\n例如:\n\n- hs的泛型是父类Hero\n- adhs 的泛型是子类ADHero\n\n那么 把adhs转换为hs能成功吗?(不能)\n\n> 父类转子类也不能\n\n\n\n","slug":"javaSE/18-泛型","published":1,"updated":"2021-08-29T15:32:01.869Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qu006dakve8q0q3khe","content":"

泛型

泛型用法

泛型的用法是在容器后面添加\\

\n
1
2
3
4
ArrayList<APHero> heros = new ArrayList<APHero>();

//简写:
ArrayList<Hero> heros2 = new ArrayList<>();
\n

Type可以是类,抽象类,接口;

\n

泛型表示这种容器,只能存放APHero,ADHero就放不进去了。

\n

创建支持泛型的类

    \n
  1. 设计一个支持泛型的栈 MyStack
  2. \n
  3. 设计这个类的时候,在类的声明上,加上一个,表示该类支持泛型。
  4. \n
  5. T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package generic;

import java.util.HashMap;
import java.util.LinkedList;

import charactor.Hero;
import property.Item;

public class MyStack<T> {
LinkedList<T> values = new LinkedList<T>();

public void push(T t) {
values.addLast(t);
}

public T pull() {
return values.removeLast();
}

public T peek() {
return values.getLast();
}

public static void main(String[] args) {
//在声明这个Stack的时候,使用泛型<Hero>就表示该Stack只能放Hero
MyStack<Hero> heroStack = new MyStack<>();
heroStack.push(new Hero());
//不能放Item
heroStack.push(new Item());

//在声明这个Stack的时候,使用泛型<Item>就表示该Stack只能放Item
MyStack<Item> itemStack = new MyStack<>();
itemStack.push(new Item());
//不能放Hero
itemStack.push(new Hero());
}

}
\n

通配符

? extends

ArrayList heroList<? extends Hero> 表示这是一个Hero泛型或者其子类泛型(可以理解为这个heroList的元素类型是:Hero或其子类类型):

\n
    \n
  • heroList 的泛型可能是Hero
  • \n
  • heroList 的泛型可能是APHero
  • \n
  • heroList 的泛型可能是ADHero
  • \n
\n

所以 可以确定的是,从heroList取出来的对象,一定是可以转型成Hero的

\n
1
2
3
4
5
6
7
ArrayList<APHero> apHeroList = new ArrayList<APHero>();
apHeroList.add(new APHero());

ArrayList<? extends Hero> heroList = apHeroList;

//可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的
Hero h = heroList.get(0);
\n
\n

注:不能往里面放东西,因为:

\n
    \n
  • 放APHero就不满足\\
  • \n
  • 放ADHero又不满足\\
  • \n
\n
1
heroList.add(new ADHero()); //编译错误,因为heroList的泛型有可能是APHero
\n
\n

? super

ArrayList heroList<? super Hero> 表示这是一个Hero或者其父类泛型:

\n
    \n
  • heroList的泛型可能是Hero
  • \n
  • heroList的泛型可能是Object
  • \n
\n

可以往里面插入Hero以及Hero的子类:

\n
    \n
  • 放Hero没问题
  • \n
  • 放APHero、ADHero也没问题
  • \n
\n
\n

但是取出来有风险,因为不确定取出来是Hero还是Object

\n
\n

\"?

\n
1
2
ArrayList<? super Hero> heroList = new ArrayList<Object>();
//? super Hero 表示 heroList的泛型是Hero或者其父类泛型Object
\n
1
2
3
4
5
6
//所以就可以插入Hero
heroList.add(new Hero());

//也可以插入Hero的子类
heroList.add(new APHero());
heroList.add(new ADHero());
\n
1
2
//但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Hero会失败
Hero h= heroList.get(0);
\n

泛型通配符【?】

泛型通配符?代表任意泛型
既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能;

\n
    \n
  • 取:只能以Object的形式取出来
  • \n
  • 放:不能往里面放对象,因为不知道到底是一个什么泛型的容器
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ArrayList<APHero> apHeroList = new ArrayList<APHero>();

//?泛型通配符,表示任意泛型
ArrayList<?> generalList = apHeroList;

//?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型
//所以只能以Object的形式取出来
Object o = generalList.get(0);

//?的缺陷2: 既然?代表任意泛型,那么既有可能是Hero,也有可能是Item
//所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去
generalList.add(new Item()); //编译错误 因为?代表任意泛型,很有可能不是Item
generalList.add(new Hero()); //编译错误 因为?代表任意泛型,很有可能不是Hero
generalList.add(new APHero()); //编译错误 因为?代表任意泛型,很有可能不是APHero
\n

总结

如果希望只取出,不插入,就使用? extends Hero

\n

如果希望只插入,不取出,就使用? super Hero

\n

如果希望,又能插入,又能取出,就不要用通配符

\n

泛型转型

对象转型

根据面向对象学习的知识,==子类转父类== 是一定可以成功的

\n

子类泛型转父类泛型

既然子类对象转父类对象是可以成功的,那么子类泛型转父类泛型能成功吗?(不能)
例如:

\n
    \n
  • hs的泛型是父类Hero
  • \n
  • adhs 的泛型是子类ADHero
  • \n
\n

那么 把adhs转换为hs能成功吗?(不能)

\n
\n

父类转子类也不能

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

泛型

泛型用法

泛型的用法是在容器后面添加\\

\n
1
2
3
4
ArrayList<APHero> heros = new ArrayList<APHero>();

//简写:
ArrayList<Hero> heros2 = new ArrayList<>();
\n

Type可以是类,抽象类,接口;

\n

泛型表示这种容器,只能存放APHero,ADHero就放不进去了。

\n

创建支持泛型的类

    \n
  1. 设计一个支持泛型的栈 MyStack
  2. \n
  3. 设计这个类的时候,在类的声明上,加上一个,表示该类支持泛型。
  4. \n
  5. T是type的缩写,也可以使用任何其他的合法的变量,比如A,B,X都可以,但是一般约定成俗使用T,代表类型。
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package generic;

import java.util.HashMap;
import java.util.LinkedList;

import charactor.Hero;
import property.Item;

public class MyStack<T> {
LinkedList<T> values = new LinkedList<T>();

public void push(T t) {
values.addLast(t);
}

public T pull() {
return values.removeLast();
}

public T peek() {
return values.getLast();
}

public static void main(String[] args) {
//在声明这个Stack的时候,使用泛型<Hero>就表示该Stack只能放Hero
MyStack<Hero> heroStack = new MyStack<>();
heroStack.push(new Hero());
//不能放Item
heroStack.push(new Item());

//在声明这个Stack的时候,使用泛型<Item>就表示该Stack只能放Item
MyStack<Item> itemStack = new MyStack<>();
itemStack.push(new Item());
//不能放Hero
itemStack.push(new Hero());
}

}
\n

通配符

? extends

ArrayList heroList<? extends Hero> 表示这是一个Hero泛型或者其子类泛型(可以理解为这个heroList的元素类型是:Hero或其子类类型):

\n
    \n
  • heroList 的泛型可能是Hero
  • \n
  • heroList 的泛型可能是APHero
  • \n
  • heroList 的泛型可能是ADHero
  • \n
\n

所以 可以确定的是,从heroList取出来的对象,一定是可以转型成Hero的

\n
1
2
3
4
5
6
7
ArrayList<APHero> apHeroList = new ArrayList<APHero>();
apHeroList.add(new APHero());

ArrayList<? extends Hero> heroList = apHeroList;

//可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的
Hero h = heroList.get(0);
\n
\n

注:不能往里面放东西,因为:

\n
    \n
  • 放APHero就不满足\\
  • \n
  • 放ADHero又不满足\\
  • \n
\n
1
heroList.add(new ADHero()); //编译错误,因为heroList的泛型有可能是APHero
\n
\n

? super

ArrayList heroList<? super Hero> 表示这是一个Hero或者其父类泛型:

\n
    \n
  • heroList的泛型可能是Hero
  • \n
  • heroList的泛型可能是Object
  • \n
\n

可以往里面插入Hero以及Hero的子类:

\n
    \n
  • 放Hero没问题
  • \n
  • 放APHero、ADHero也没问题
  • \n
\n
\n

但是取出来有风险,因为不确定取出来是Hero还是Object

\n
\n

\"?

\n
1
2
ArrayList<? super Hero> heroList = new ArrayList<Object>();
//? super Hero 表示 heroList的泛型是Hero或者其父类泛型Object
\n
1
2
3
4
5
6
//所以就可以插入Hero
heroList.add(new Hero());

//也可以插入Hero的子类
heroList.add(new APHero());
heroList.add(new ADHero());
\n
1
2
//但是,不能从里面取数据出来,因为其泛型可能是Object,而Object是强转Hero会失败
Hero h= heroList.get(0);
\n

泛型通配符【?】

泛型通配符?代表任意泛型
既然?代表任意泛型,那么换句话说,这个容器什么泛型都有可能;

\n
    \n
  • 取:只能以Object的形式取出来
  • \n
  • 放:不能往里面放对象,因为不知道到底是一个什么泛型的容器
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ArrayList<APHero> apHeroList = new ArrayList<APHero>();

//?泛型通配符,表示任意泛型
ArrayList<?> generalList = apHeroList;

//?的缺陷1: 既然?代表任意泛型,那么换句话说,你就不知道这个容器里面是什么类型
//所以只能以Object的形式取出来
Object o = generalList.get(0);

//?的缺陷2: 既然?代表任意泛型,那么既有可能是Hero,也有可能是Item
//所以,放哪种对象进去,都有风险,结果就什么什么类型的对象,都不能放进去
generalList.add(new Item()); //编译错误 因为?代表任意泛型,很有可能不是Item
generalList.add(new Hero()); //编译错误 因为?代表任意泛型,很有可能不是Hero
generalList.add(new APHero()); //编译错误 因为?代表任意泛型,很有可能不是APHero
\n

总结

如果希望只取出,不插入,就使用? extends Hero

\n

如果希望只插入,不取出,就使用? super Hero

\n

如果希望,又能插入,又能取出,就不要用通配符

\n

泛型转型

对象转型

根据面向对象学习的知识,==子类转父类== 是一定可以成功的

\n

子类泛型转父类泛型

既然子类对象转父类对象是可以成功的,那么子类泛型转父类泛型能成功吗?(不能)
例如:

\n
    \n
  • hs的泛型是父类Hero
  • \n
  • adhs 的泛型是子类ADHero
  • \n
\n

那么 把adhs转换为hs能成功吗?(不能)

\n
\n

父类转子类也不能

\n
\n"},{"title":"19-多线程系列","abbrlink":"c5d59ae","date":"2021-02-19T02:11:21.000Z","description":"java 多线程介绍(5种状态:新生、就绪、运行、阻塞、死亡)与使用、锁的使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 多线程系列\n\n## 线程状态\n\n\n\n一个线程对象在它的生命周期内,需要经历5个状态。\n\n![图11-4 线程生命周期图.png](https://www.sxt.cn/360shop/Public/admin/UEditor/20170526/1495787690411518.png)\n\n### 新生状态(New)\n\n用new关键字建立一个线程对象后,该线程对象就处于新生状态。\n\n处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。\n\n### 就绪状态(Runnable)\n\n处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。\n\n**就绪状态并不是执行状态**,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:\n\n1. 新建线程:调用start()方法,进入就绪状态;\n\n2. 阻塞线程:阻塞解除,进入就绪状态;\n\n3. 运行线程:调用yield()方法,直接进入就绪状态;\n\n4. 运行线程:JVM将CPU资源从本线程切换到其他线程。\n\n\n\n### 运行状态(Running)\n\n在运行状态的线程**执行自己run方法中**的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。\n\n如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。\n\n\n\n### 阻塞状态(Blocked)\n\n阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。\n\n有4种原因会导致阻塞:\n\n1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。\n\n2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。\n\n3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。\n\n4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法\n\n### 死亡状态(Terminated)\n\n死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:\n\n1. 正常运行的线程完成了它run()方法内的全部工作\n\n2. 线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程\n\n (注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。\n\n当一个线程进入死亡状态以后,就不能再回到其它状态了。\n\n\n\n\n\n## 终止线程\n\n终止线程一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。\n\n通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程。\n\n\n\n```java\npackage com.myThread;\n\npublic class TestThread implements Runnable{\n String name;\n boolean live = true;// 标记变量,表示线程是否可中止;\n public TestThread(String name) {\n super();\n this.name = name;\n }\n public void run() {\n int i = 0;\n //当live的值是true时,继续线程体;false则结束循环,继而终止线程体;\n while (live) {\n System.out.println(name + (i++));\n }\n }\n public void terminate() {\n live = false;\n }\n\n public static void main(String[] args) {\n TestThread ttc = new TestThread(\"线程A:\");\n Thread t1 = new Thread(ttc);// 新生状态\n t1.start();// 就绪状态\n for (int i = 0; i < 10; i++) {\n System.out.println(\"主线程\" + i);\n }\n ttc.terminate();\n System.out.println(\"ttc stop!\");\n }\n}\n\n\n\n```\n\n该程序中通过主线程控制live的值,当主线程把live置为 `false`时,run()方法停止执行,子线程终止运行\n\n\n\n## 暂停线程\n\n暂停线程执行常用的方法有sleep()和yield()方法\n\n这两个方法的区别是:\n\n1. sleep()方法:可以让正在运行的线程进入阻塞状态,直到**休眠时间**满了,进入就绪状态。\n\n > 注意:是休眠期到了后才会进入就绪状态,参与竞争获取CPU使用权\n\n2. yield()方法:可以让正在运行的线程**直接进入**就绪状态,**让出CPU的使用权**。\n\n > 注意:由于没有标出**让出**的时间,所以下一刻就会立即进入竞争获取CPU的就绪状态\n\n\n\n## join()方法\n\n`b.join()`可以将一个线程b插入到当前线程a中,这时a线程需要等待b执行完才会继续\n\n\n\n## 线程基本信息获取\n\n\n\n```java\nisAlive();\ngetPriority();\t//获取线程优先级,默认为5\nsetPriority(); //设置线程优先级数值(int)\n\nsetName();\ngetName();\n\ncurrentThread();\t\t\t//获得当前线程\n```\n\n> 注意:**优先级**低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。\n\n\n\n\n\n## 线程同步\n\n线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。\n\n由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。\n\n由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是`synchronized`关键字,它包括两种用法:\n\n- synchronized 方法\n- synchronized 块\n\n### synchronized 方法\n\n```java\npublic synchronized void func(int a);\n```\n\n\n\nsynchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就**独占**该锁,直到**从该方法返回**时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。\n\n\n\n### synchronized块\n\nsynchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。\n\nJava 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,**提高效率**。\n\nsynchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:\n\n```java\nsynchronized(syncObject){ \n   //允许访问控制的代码 \n}\n```\n\n表示在操控 syncObject时,每次只能控制一次,如果多个线程同时执行到这一条语句,则同一时间只能有一个线程操控syncObject\n\n\n\n## 死锁问题\n\n死锁:\n\n多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。\n\n\n\n\n\n","source":"_posts/javaSE/19-多线程系列.md","raw":"---\ntitle: 19-多线程系列\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: c5d59ae\ndate: 2021-02-19 10:11:21\ndescription: java 多线程介绍(5种状态:新生、就绪、运行、阻塞、死亡)与使用、锁的使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 多线程系列\n\n## 线程状态\n\n\n\n一个线程对象在它的生命周期内,需要经历5个状态。\n\n![图11-4 线程生命周期图.png](https://www.sxt.cn/360shop/Public/admin/UEditor/20170526/1495787690411518.png)\n\n### 新生状态(New)\n\n用new关键字建立一个线程对象后,该线程对象就处于新生状态。\n\n处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。\n\n### 就绪状态(Runnable)\n\n处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。\n\n**就绪状态并不是执行状态**,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:\n\n1. 新建线程:调用start()方法,进入就绪状态;\n\n2. 阻塞线程:阻塞解除,进入就绪状态;\n\n3. 运行线程:调用yield()方法,直接进入就绪状态;\n\n4. 运行线程:JVM将CPU资源从本线程切换到其他线程。\n\n\n\n### 运行状态(Running)\n\n在运行状态的线程**执行自己run方法中**的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。\n\n如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。\n\n\n\n### 阻塞状态(Blocked)\n\n阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。\n\n有4种原因会导致阻塞:\n\n1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。\n\n2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。\n\n3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。\n\n4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法\n\n### 死亡状态(Terminated)\n\n死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:\n\n1. 正常运行的线程完成了它run()方法内的全部工作\n\n2. 线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程\n\n (注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。\n\n当一个线程进入死亡状态以后,就不能再回到其它状态了。\n\n\n\n\n\n## 终止线程\n\n终止线程一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。\n\n通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程。\n\n\n\n```java\npackage com.myThread;\n\npublic class TestThread implements Runnable{\n String name;\n boolean live = true;// 标记变量,表示线程是否可中止;\n public TestThread(String name) {\n super();\n this.name = name;\n }\n public void run() {\n int i = 0;\n //当live的值是true时,继续线程体;false则结束循环,继而终止线程体;\n while (live) {\n System.out.println(name + (i++));\n }\n }\n public void terminate() {\n live = false;\n }\n\n public static void main(String[] args) {\n TestThread ttc = new TestThread(\"线程A:\");\n Thread t1 = new Thread(ttc);// 新生状态\n t1.start();// 就绪状态\n for (int i = 0; i < 10; i++) {\n System.out.println(\"主线程\" + i);\n }\n ttc.terminate();\n System.out.println(\"ttc stop!\");\n }\n}\n\n\n\n```\n\n该程序中通过主线程控制live的值,当主线程把live置为 `false`时,run()方法停止执行,子线程终止运行\n\n\n\n## 暂停线程\n\n暂停线程执行常用的方法有sleep()和yield()方法\n\n这两个方法的区别是:\n\n1. sleep()方法:可以让正在运行的线程进入阻塞状态,直到**休眠时间**满了,进入就绪状态。\n\n > 注意:是休眠期到了后才会进入就绪状态,参与竞争获取CPU使用权\n\n2. yield()方法:可以让正在运行的线程**直接进入**就绪状态,**让出CPU的使用权**。\n\n > 注意:由于没有标出**让出**的时间,所以下一刻就会立即进入竞争获取CPU的就绪状态\n\n\n\n## join()方法\n\n`b.join()`可以将一个线程b插入到当前线程a中,这时a线程需要等待b执行完才会继续\n\n\n\n## 线程基本信息获取\n\n\n\n```java\nisAlive();\ngetPriority();\t//获取线程优先级,默认为5\nsetPriority(); //设置线程优先级数值(int)\n\nsetName();\ngetName();\n\ncurrentThread();\t\t\t//获得当前线程\n```\n\n> 注意:**优先级**低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。\n\n\n\n\n\n## 线程同步\n\n线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。\n\n由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。\n\n由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是`synchronized`关键字,它包括两种用法:\n\n- synchronized 方法\n- synchronized 块\n\n### synchronized 方法\n\n```java\npublic synchronized void func(int a);\n```\n\n\n\nsynchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就**独占**该锁,直到**从该方法返回**时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。\n\n\n\n### synchronized块\n\nsynchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。\n\nJava 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,**提高效率**。\n\nsynchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:\n\n```java\nsynchronized(syncObject){ \n   //允许访问控制的代码 \n}\n```\n\n表示在操控 syncObject时,每次只能控制一次,如果多个线程同时执行到这一条语句,则同一时间只能有一个线程操控syncObject\n\n\n\n## 死锁问题\n\n死锁:\n\n多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。\n\n\n\n\n\n","slug":"javaSE/19-多线程系列","published":1,"updated":"2021-08-29T15:32:05.689Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qv006hakve2qm4crpn","content":"

多线程系列

线程状态

一个线程对象在它的生命周期内,需要经历5个状态。

\n

\"图11-4

\n

新生状态(New)

用new关键字建立一个线程对象后,该线程对象就处于新生状态。

\n

处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。

\n

就绪状态(Runnable)

处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。

\n

就绪状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:

\n
    \n
  1. 新建线程:调用start()方法,进入就绪状态;

    \n
  2. \n
  3. 阻塞线程:阻塞解除,进入就绪状态;

    \n
  4. \n
  5. 运行线程:调用yield()方法,直接进入就绪状态;

    \n
  6. \n
  7. 运行线程:JVM将CPU资源从本线程切换到其他线程。

    \n
  8. \n
\n

运行状态(Running)

在运行状态的线程执行自己run方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。

\n

如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。

\n

阻塞状态(Blocked)

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。

\n

有4种原因会导致阻塞:

\n
    \n
  1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。

    \n
  2. \n
  3. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。

    \n
  4. \n
  5. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。

    \n
  6. \n
  7. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法

    \n
  8. \n
\n

死亡状态(Terminated)

死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:

\n
    \n
  1. 正常运行的线程完成了它run()方法内的全部工作

    \n
  2. \n
  3. 线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程

    \n

    (注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。

    \n
  4. \n
\n

当一个线程进入死亡状态以后,就不能再回到其它状态了。

\n

终止线程

终止线程一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。

\n

通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.myThread;

public class TestThread implements Runnable{
String name;
boolean live = true;// 标记变量,表示线程是否可中止;
public TestThread(String name) {
super();
this.name = name;
}
public void run() {
int i = 0;
//当live的值是true时,继续线程体;false则结束循环,继而终止线程体;
while (live) {
System.out.println(name + (i++));
}
}
public void terminate() {
live = false;
}

public static void main(String[] args) {
TestThread ttc = new TestThread("线程A:");
Thread t1 = new Thread(ttc);// 新生状态
t1.start();// 就绪状态
for (int i = 0; i < 10; i++) {
System.out.println("主线程" + i);
}
ttc.terminate();
System.out.println("ttc stop!");
}
}



\n

该程序中通过主线程控制live的值,当主线程把live置为 false时,run()方法停止执行,子线程终止运行

\n

暂停线程

暂停线程执行常用的方法有sleep()和yield()方法

\n

这两个方法的区别是:

\n
    \n
  1. sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。

    \n
    \n

    注意:是休眠期到了后才会进入就绪状态,参与竞争获取CPU使用权

    \n
    \n
  2. \n
  3. yield()方法:可以让正在运行的线程直接进入就绪状态,让出CPU的使用权

    \n
    \n

    注意:由于没有标出让出的时间,所以下一刻就会立即进入竞争获取CPU的就绪状态

    \n
    \n
  4. \n
\n

join()方法

b.join()可以将一个线程b插入到当前线程a中,这时a线程需要等待b执行完才会继续

\n

线程基本信息获取

1
2
3
4
5
6
7
8
isAlive();
getPriority();\t//获取线程优先级,默认为5
setPriority(); //设置线程优先级数值(int)

setName();
getName();

currentThread();\t\t\t//获得当前线程
\n
\n

注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。

\n
\n

线程同步

线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。

\n

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。

\n

由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:

\n
    \n
  • synchronized 方法
  • \n
  • synchronized 块
  • \n
\n

synchronized 方法

1
public synchronized void func(int a);
\n

synchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

\n

synchronized块

synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。

\n

Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率

\n

synchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:

\n
1
2
3
synchronized(syncObject){ 
   //允许访问控制的代码
}
\n

表示在操控 syncObject时,每次只能控制一次,如果多个线程同时执行到这一条语句,则同一时间只能有一个线程操控syncObject

\n

死锁问题

死锁:

\n

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

多线程系列

线程状态

一个线程对象在它的生命周期内,需要经历5个状态。

\n

\"图11-4

\n

新生状态(New)

用new关键字建立一个线程对象后,该线程对象就处于新生状态。

\n

处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。

\n

就绪状态(Runnable)

处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。

\n

就绪状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:

\n
    \n
  1. 新建线程:调用start()方法,进入就绪状态;

    \n
  2. \n
  3. 阻塞线程:阻塞解除,进入就绪状态;

    \n
  4. \n
  5. 运行线程:调用yield()方法,直接进入就绪状态;

    \n
  6. \n
  7. 运行线程:JVM将CPU资源从本线程切换到其他线程。

    \n
  8. \n
\n

运行状态(Running)

在运行状态的线程执行自己run方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。

\n

如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。

\n

阻塞状态(Blocked)

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。

\n

有4种原因会导致阻塞:

\n
    \n
  1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。

    \n
  2. \n
  3. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。

    \n
  4. \n
  5. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。

    \n
  6. \n
  7. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法

    \n
  8. \n
\n

死亡状态(Terminated)

死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:

\n
    \n
  1. 正常运行的线程完成了它run()方法内的全部工作

    \n
  2. \n
  3. 线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程

    \n

    (注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。

    \n
  4. \n
\n

当一个线程进入死亡状态以后,就不能再回到其它状态了。

\n

终止线程

终止线程一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。

\n

通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.myThread;

public class TestThread implements Runnable{
String name;
boolean live = true;// 标记变量,表示线程是否可中止;
public TestThread(String name) {
super();
this.name = name;
}
public void run() {
int i = 0;
//当live的值是true时,继续线程体;false则结束循环,继而终止线程体;
while (live) {
System.out.println(name + (i++));
}
}
public void terminate() {
live = false;
}

public static void main(String[] args) {
TestThread ttc = new TestThread("线程A:");
Thread t1 = new Thread(ttc);// 新生状态
t1.start();// 就绪状态
for (int i = 0; i < 10; i++) {
System.out.println("主线程" + i);
}
ttc.terminate();
System.out.println("ttc stop!");
}
}



\n

该程序中通过主线程控制live的值,当主线程把live置为 false时,run()方法停止执行,子线程终止运行

\n

暂停线程

暂停线程执行常用的方法有sleep()和yield()方法

\n

这两个方法的区别是:

\n
    \n
  1. sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。

    \n
    \n

    注意:是休眠期到了后才会进入就绪状态,参与竞争获取CPU使用权

    \n
    \n
  2. \n
  3. yield()方法:可以让正在运行的线程直接进入就绪状态,让出CPU的使用权

    \n
    \n

    注意:由于没有标出让出的时间,所以下一刻就会立即进入竞争获取CPU的就绪状态

    \n
    \n
  4. \n
\n

join()方法

b.join()可以将一个线程b插入到当前线程a中,这时a线程需要等待b执行完才会继续

\n

线程基本信息获取

1
2
3
4
5
6
7
8
isAlive();
getPriority();\t//获取线程优先级,默认为5
setPriority(); //设置线程优先级数值(int)

setName();
getName();

currentThread();\t\t\t//获得当前线程
\n
\n

注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。

\n
\n

线程同步

线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。

\n

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。

\n

由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:

\n
    \n
  • synchronized 方法
  • \n
  • synchronized 块
  • \n
\n

synchronized 方法

1
public synchronized void func(int a);
\n

synchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

\n

synchronized块

synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。

\n

Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率

\n

synchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:

\n
1
2
3
synchronized(syncObject){ 
   //允许访问控制的代码
}
\n

表示在操控 syncObject时,每次只能控制一次,如果多个线程同时执行到这一条语句,则同一时间只能有一个线程操控syncObject

\n

死锁问题

死锁:

\n

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。

\n"},{"title":"2.2-访问修饰符","abbrlink":"51a2ab89","date":"2021-02-07T04:12:26.000Z","description":"Java的访问修饰符有4个,默认没有写出来,是default,其他3个是 public、private、protected","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 访问修饰符\n\n## 区分:\n\n有 `public`、`private`、`protected`\n\n没有修饰符即为默认:`package/friendly/default`\n\n- private:只有类自身可以访问;由该类实例的对象不能访问,子类不能继承\n\n- public:所有类均可以继承、访问:(同包/不同包)子类可继承;(同包/不同包)类可以访问\n\n- protected:不同包且没有继承关系的类不能访问\n\n- 没有修饰符(package):只能在自己包使用;不同包不能访问、继承\n\n- 总结(红色字体表示不可行)![总结](https://stepimagewm.how2j.cn/612.png)\n\n ​\t\t\t\t\n\n> 下面以Hero为研究对象,弄清楚各个类之间的关系\n>\n> **自身:**指的是Hero自己\n> **同包子类:**ADHero这个类是Hero的子类,并且和Hero处于同一个包下\n> **不同包子类:**Support这个类是Hero的子类,但是在另一个包下\n> **同包类:** GiantDragon 这个类和Hero是**同一个包**,但是彼此没有继承关系\n> **其他类:**Item这个类,**在不同包**,也没有继承关系的类\n>\n> ![](https://stepimagewm.how2j.cn/605.png)\n\n\n\n## 什么情况使用什么修饰符\n\n> 1. 属性通常使用private封装起来\n>\n> 2. 方法一般使用public用于被调用\n>\n> 3. 会被子类继承的方法,通常使用protected\n>\n> 4. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西\n\n---\n\n> **作用范围最小原则:**\n> 简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要**露出来的**,就不用**露出来**了","source":"_posts/javaSE/2.2-访问修饰符.md","raw":"---\ntitle: 2.2-访问修饰符\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 51a2ab89\ndate: 2021-02-07 12:12:26\ndescription: Java的访问修饰符有4个,默认没有写出来,是default,其他3个是 public、private、protected\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 访问修饰符\n\n## 区分:\n\n有 `public`、`private`、`protected`\n\n没有修饰符即为默认:`package/friendly/default`\n\n- private:只有类自身可以访问;由该类实例的对象不能访问,子类不能继承\n\n- public:所有类均可以继承、访问:(同包/不同包)子类可继承;(同包/不同包)类可以访问\n\n- protected:不同包且没有继承关系的类不能访问\n\n- 没有修饰符(package):只能在自己包使用;不同包不能访问、继承\n\n- 总结(红色字体表示不可行)![总结](https://stepimagewm.how2j.cn/612.png)\n\n ​\t\t\t\t\n\n> 下面以Hero为研究对象,弄清楚各个类之间的关系\n>\n> **自身:**指的是Hero自己\n> **同包子类:**ADHero这个类是Hero的子类,并且和Hero处于同一个包下\n> **不同包子类:**Support这个类是Hero的子类,但是在另一个包下\n> **同包类:** GiantDragon 这个类和Hero是**同一个包**,但是彼此没有继承关系\n> **其他类:**Item这个类,**在不同包**,也没有继承关系的类\n>\n> ![](https://stepimagewm.how2j.cn/605.png)\n\n\n\n## 什么情况使用什么修饰符\n\n> 1. 属性通常使用private封装起来\n>\n> 2. 方法一般使用public用于被调用\n>\n> 3. 会被子类继承的方法,通常使用protected\n>\n> 4. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西\n\n---\n\n> **作用范围最小原则:**\n> 简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要**露出来的**,就不用**露出来**了","slug":"javaSE/2.2-访问修饰符","published":1,"updated":"2021-08-29T15:30:38.950Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qw006kakvec7h0d155","content":"

访问修饰符

区分:

publicprivateprotected

\n

没有修饰符即为默认:package/friendly/default

\n
    \n
  • private:只有类自身可以访问;由该类实例的对象不能访问,子类不能继承

    \n
  • \n
  • public:所有类均可以继承、访问:(同包/不同包)子类可继承;(同包/不同包)类可以访问

    \n
  • \n
  • protected:不同包且没有继承关系的类不能访问

    \n
  • \n
  • 没有修饰符(package):只能在自己包使用;不同包不能访问、继承

    \n
  • \n
  • 总结(红色字体表示不可行)\"总结\"

    \n

    \n
  • \n
\n
\n

下面以Hero为研究对象,弄清楚各个类之间的关系

\n

自身:指的是Hero自己
同包子类:ADHero这个类是Hero的子类,并且和Hero处于同一个包下
不同包子类:Support这个类是Hero的子类,但是在另一个包下
同包类: GiantDragon 这个类和Hero是同一个包,但是彼此没有继承关系
其他类:Item这个类,在不同包,也没有继承关系的类

\n

\"\"

\n
\n

什么情况使用什么修饰符

\n
    \n
  1. 属性通常使用private封装起来

    \n
  2. \n
  3. 方法一般使用public用于被调用

    \n
  4. \n
  5. 会被子类继承的方法,通常使用protected

    \n
  6. \n
  7. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西

    \n
  8. \n
\n
\n
\n
\n

作用范围最小原则:
简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

访问修饰符

区分:

publicprivateprotected

\n

没有修饰符即为默认:package/friendly/default

\n
    \n
  • private:只有类自身可以访问;由该类实例的对象不能访问,子类不能继承

    \n
  • \n
  • public:所有类均可以继承、访问:(同包/不同包)子类可继承;(同包/不同包)类可以访问

    \n
  • \n
  • protected:不同包且没有继承关系的类不能访问

    \n
  • \n
  • 没有修饰符(package):只能在自己包使用;不同包不能访问、继承

    \n
  • \n
  • 总结(红色字体表示不可行)\"总结\"

    \n

    \n
  • \n
\n
\n

下面以Hero为研究对象,弄清楚各个类之间的关系

\n

自身:指的是Hero自己
同包子类:ADHero这个类是Hero的子类,并且和Hero处于同一个包下
不同包子类:Support这个类是Hero的子类,但是在另一个包下
同包类: GiantDragon 这个类和Hero是同一个包,但是彼此没有继承关系
其他类:Item这个类,在不同包,也没有继承关系的类

\n

\"\"

\n
\n

什么情况使用什么修饰符

\n
    \n
  1. 属性通常使用private封装起来

    \n
  2. \n
  3. 方法一般使用public用于被调用

    \n
  4. \n
  5. 会被子类继承的方法,通常使用protected

    \n
  6. \n
  7. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西

    \n
  8. \n
\n
\n
\n
\n

作用范围最小原则:
简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来

\n
\n"},{"title":"20-反射机制","abbrlink":"c9f91dab","date":"2021-02-20T04:13:21.000Z","description":"java反射的使用(获取类对象、创建对象、属性访问、方法调用等)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# Java反射机制\n\n\n\n## 获取类对象\n\n### 类对象\n\n**类对象**,用于描述这种类,都有什么属性,什么方法的\n\n\n\n### 获取类对象\n\n- Class.forName();\n\n- Hero.class\n\n (注:Hero是自定义类的类名)\n\n- new Hero().getClass()\n\n在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。\n\n\n\n```java\nString className = \"charactor.Hero\";\n\nClass pClass1 = Class.forName(className);\nClass pClass2 = Hero.class;\nClass pClass3 = new Hero().getClass();\n\nSystem.out.println(pClass1 == pClass2);//true\nSystem.out.println(pClass1 == pClass3);//true\n```\n\n\n\n### 获取类对象,会导致类属性的初始化\n\n无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。\n\n(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)\n\n\n\n## 创建类对象\n\n使用反射机制创建类对象,3个步骤\n\n1. 获取类对象\n2. 获取构造器\n3. 通过构造器实例化\n\n```java\n//使用反射的方式创建对象\nString className = \"charactor.Hero\";\n//类对象\nClass pClass = Class.forName(className);\n\n//构造器\nConstructor c = pClass.getConstructor();\n\n//通过构造器实例化\nHero h2 = (Hero) c.newInstance();\n```\n\n\n\n## 属性访问\n\n### 用法\n\n使用反射机制获取并修改类对象属性(也叫字段`Field`),4个步骤\n\n1. 导包\n2. 获取类对象\n3. 获取属性(**字段**)—— `getDeclaredField`\n4. 修改指定对象的属性值—— `set`\n\n```java\nimport java.lang.reflect.Field;\n\nHero h = new Hero();\n\nClass c = h.getClass();\n\nField f = c.getDeclaredField(\"name\");\n\nf1.set(h, \"teemo\");//修改这个字段的值\n```\n\n\n\n### 区分 getField 与 getDeclaredField\n\n- 这两个方法都是用于获取字段\n\n- `getField` **只能获取**public的,包括**从父类继承**来的字段。\n\n- `getDeclaredField` 可以获取本类所有的字段,**包括private**的,但是**不能获取继承**来的字段。\n\n **注**: 这里只能获取到private的**字段**,但并不能访问该private字段的**值**,除非加上**setAccessible(true)**\n\n\n\n## 方法获取\n\n1. 导包\n2. 获取类对象\n3. 获取到方法—— `getMethod`\n4. 调用指定对象的方法—— `invoke`\n\n```java\nimport java.lang.reflect.Method;\n\nHero h = new Hero();\n \n// 获取这个名字叫做setName,参数类型是String的方法\nMethod m = h.getClass().getMethod(\"setName\", String.class);\n\n// 对h对象,调用这个方法\nm.invoke(h, \"garent\");\n```\n\n","source":"_posts/javaSE/20-反射机制.md","raw":"---\ntitle: 20-反射机制\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: c9f91dab\ndate: 2021-02-20 12:13:21\ndescription: java反射的使用(获取类对象、创建对象、属性访问、方法调用等)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# Java反射机制\n\n\n\n## 获取类对象\n\n### 类对象\n\n**类对象**,用于描述这种类,都有什么属性,什么方法的\n\n\n\n### 获取类对象\n\n- Class.forName();\n\n- Hero.class\n\n (注:Hero是自定义类的类名)\n\n- new Hero().getClass()\n\n在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。\n\n\n\n```java\nString className = \"charactor.Hero\";\n\nClass pClass1 = Class.forName(className);\nClass pClass2 = Hero.class;\nClass pClass3 = new Hero().getClass();\n\nSystem.out.println(pClass1 == pClass2);//true\nSystem.out.println(pClass1 == pClass3);//true\n```\n\n\n\n### 获取类对象,会导致类属性的初始化\n\n无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。\n\n(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)\n\n\n\n## 创建类对象\n\n使用反射机制创建类对象,3个步骤\n\n1. 获取类对象\n2. 获取构造器\n3. 通过构造器实例化\n\n```java\n//使用反射的方式创建对象\nString className = \"charactor.Hero\";\n//类对象\nClass pClass = Class.forName(className);\n\n//构造器\nConstructor c = pClass.getConstructor();\n\n//通过构造器实例化\nHero h2 = (Hero) c.newInstance();\n```\n\n\n\n## 属性访问\n\n### 用法\n\n使用反射机制获取并修改类对象属性(也叫字段`Field`),4个步骤\n\n1. 导包\n2. 获取类对象\n3. 获取属性(**字段**)—— `getDeclaredField`\n4. 修改指定对象的属性值—— `set`\n\n```java\nimport java.lang.reflect.Field;\n\nHero h = new Hero();\n\nClass c = h.getClass();\n\nField f = c.getDeclaredField(\"name\");\n\nf1.set(h, \"teemo\");//修改这个字段的值\n```\n\n\n\n### 区分 getField 与 getDeclaredField\n\n- 这两个方法都是用于获取字段\n\n- `getField` **只能获取**public的,包括**从父类继承**来的字段。\n\n- `getDeclaredField` 可以获取本类所有的字段,**包括private**的,但是**不能获取继承**来的字段。\n\n **注**: 这里只能获取到private的**字段**,但并不能访问该private字段的**值**,除非加上**setAccessible(true)**\n\n\n\n## 方法获取\n\n1. 导包\n2. 获取类对象\n3. 获取到方法—— `getMethod`\n4. 调用指定对象的方法—— `invoke`\n\n```java\nimport java.lang.reflect.Method;\n\nHero h = new Hero();\n \n// 获取这个名字叫做setName,参数类型是String的方法\nMethod m = h.getClass().getMethod(\"setName\", String.class);\n\n// 对h对象,调用这个方法\nm.invoke(h, \"garent\");\n```\n\n","slug":"javaSE/20-反射机制","published":1,"updated":"2021-08-29T15:32:10.453Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qx006oakveh6kj9q64","content":"

Java反射机制

获取类对象

类对象

类对象,用于描述这种类,都有什么属性,什么方法的

\n

获取类对象

    \n
  • Class.forName();

    \n
  • \n
  • Hero.class

    \n

    (注:Hero是自定义类的类名)

    \n
  • \n
  • new Hero().getClass()

    \n
  • \n
\n

在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。

\n
1
2
3
4
5
6
7
8
String className = "charactor.Hero";

Class pClass1 = Class.forName(className);
Class pClass2 = Hero.class;
Class pClass3 = new Hero().getClass();

System.out.println(pClass1 == pClass2);//true
System.out.println(pClass1 == pClass3);//true
\n

获取类对象,会导致类属性的初始化

无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。

\n

(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)

\n

创建类对象

使用反射机制创建类对象,3个步骤

\n
    \n
  1. 获取类对象
  2. \n
  3. 获取构造器
  4. \n
  5. 通过构造器实例化
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
//使用反射的方式创建对象
String className = "charactor.Hero";
//类对象
Class pClass = Class.forName(className);

//构造器
Constructor c = pClass.getConstructor();

//通过构造器实例化
Hero h2 = (Hero) c.newInstance();
\n

属性访问

用法

使用反射机制获取并修改类对象属性(也叫字段Field),4个步骤

\n
    \n
  1. 导包
  2. \n
  3. 获取类对象
  4. \n
  5. 获取属性(字段)—— getDeclaredField
  6. \n
  7. 修改指定对象的属性值—— set
  8. \n
\n
1
2
3
4
5
6
7
8
9
import java.lang.reflect.Field;

Hero h = new Hero();

Class c = h.getClass();

Field f = c.getDeclaredField("name");

f1.set(h, "teemo");//修改这个字段的值
\n

区分 getField 与 getDeclaredField

    \n
  • 这两个方法都是用于获取字段

    \n
  • \n
  • getField 只能获取public的,包括从父类继承来的字段。

    \n
  • \n
  • getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。

    \n

    : 这里只能获取到private的字段,但并不能访问该private字段的,除非加上setAccessible(true)

    \n
  • \n
\n

方法获取

    \n
  1. 导包
  2. \n
  3. 获取类对象
  4. \n
  5. 获取到方法—— getMethod
  6. \n
  7. 调用指定对象的方法—— invoke
  8. \n
\n
1
2
3
4
5
6
7
8
9
import java.lang.reflect.Method;

Hero h = new Hero();

// 获取这个名字叫做setName,参数类型是String的方法
Method m = h.getClass().getMethod("setName", String.class);

// 对h对象,调用这个方法
m.invoke(h, "garent");
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Java反射机制

获取类对象

类对象

类对象,用于描述这种类,都有什么属性,什么方法的

\n

获取类对象

    \n
  • Class.forName();

    \n
  • \n
  • Hero.class

    \n

    (注:Hero是自定义类的类名)

    \n
  • \n
  • new Hero().getClass()

    \n
  • \n
\n

在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。

\n
1
2
3
4
5
6
7
8
String className = "charactor.Hero";

Class pClass1 = Class.forName(className);
Class pClass2 = Hero.class;
Class pClass3 = new Hero().getClass();

System.out.println(pClass1 == pClass2);//true
System.out.println(pClass1 == pClass3);//true
\n

获取类对象,会导致类属性的初始化

无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。

\n

(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)

\n

创建类对象

使用反射机制创建类对象,3个步骤

\n
    \n
  1. 获取类对象
  2. \n
  3. 获取构造器
  4. \n
  5. 通过构造器实例化
  6. \n
\n
1
2
3
4
5
6
7
8
9
10
//使用反射的方式创建对象
String className = "charactor.Hero";
//类对象
Class pClass = Class.forName(className);

//构造器
Constructor c = pClass.getConstructor();

//通过构造器实例化
Hero h2 = (Hero) c.newInstance();
\n

属性访问

用法

使用反射机制获取并修改类对象属性(也叫字段Field),4个步骤

\n
    \n
  1. 导包
  2. \n
  3. 获取类对象
  4. \n
  5. 获取属性(字段)—— getDeclaredField
  6. \n
  7. 修改指定对象的属性值—— set
  8. \n
\n
1
2
3
4
5
6
7
8
9
import java.lang.reflect.Field;

Hero h = new Hero();

Class c = h.getClass();

Field f = c.getDeclaredField("name");

f1.set(h, "teemo");//修改这个字段的值
\n

区分 getField 与 getDeclaredField

    \n
  • 这两个方法都是用于获取字段

    \n
  • \n
  • getField 只能获取public的,包括从父类继承来的字段。

    \n
  • \n
  • getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。

    \n

    : 这里只能获取到private的字段,但并不能访问该private字段的,除非加上setAccessible(true)

    \n
  • \n
\n

方法获取

    \n
  1. 导包
  2. \n
  3. 获取类对象
  4. \n
  5. 获取到方法—— getMethod
  6. \n
  7. 调用指定对象的方法—— invoke
  8. \n
\n
1
2
3
4
5
6
7
8
9
import java.lang.reflect.Method;

Hero h = new Hero();

// 获取这个名字叫做setName,参数类型是String的方法
Method m = h.getClass().getMethod("setName", String.class);

// 对h对象,调用这个方法
m.invoke(h, "garent");
\n"},{"title":"21-注解","abbrlink":"170e1590","date":"2021-02-20T08:42:36.000Z","description":"java的注解介绍与使用(元注解、自定义注解)","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# Java注解Anotation\n\n## 什么是注解\n\n注解是jdk5.0开始引进的新技术\n\n\n\n### 作用是什么\n\n1. 注解的作用类似注释,但注释是给人(程序员)看的,而注解是给编译器看的。\n\n2. 注解通过标记包、类、字段、方法、局部变量、方法参数等元素据,告诉jvm这些元素据的信息\n\n\n\n### 格式\n\n\n\n以 `@注解名` 的格式存在,如:\n\n```java\n@override\n\n@suppressWarnings(value=\"unchecked\")\n```\n\n\n\n\n\n\n\n\n\n\n\n## 元注解\n\n\n\n元注解即用来定义注解的注解,也就是在你创建注解的时候会用到,Java有4个类型的元注解:\n\n```java\n@Target\n@Retention\n@Documented \n@Inherited\n```\n\n\n\n1. `@Target` 表示该注解用于什么地方,可以是包、类、方法......,在枚举类 `ElemenetType`中 有说明:\n\n ```java\n public enum ElementType {\n /** Class, interface (including annotation type), or enum declaration */\n TYPE,\n \n /** Field declaration (includes enum constants) */\n FIELD,\n \n /** Method declaration */\n METHOD,\n \n /** Formal parameter declaration */\n PARAMETER,\n \n /** Constructor declaration */\n CONSTRUCTOR,\n \n /** Local variable declaration */\n LOCAL_VARIABLE,\n \n /** Annotation type declaration */\n ANNOTATION_TYPE,\n \n /** Package declaration */\n PACKAGE,\n \n /**\n * Type parameter declaration\n *\n * @since 1.8\n */\n TYPE_PARAMETER,\n \n /**\n * Use of a type\n *\n * @since 1.8\n */\n TYPE_USE\n }\n ```\n\n 例如创建一个test1注解:\n\n ```java\n @Target({ElementType.METHOD, ElementType.TYPE}) //可以用在方法、class中\n @interface Test1{\n String value() default \"\"; //参数,默认为空 \n }\n ```\n\n \n\n2. `@Retention` 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:\n\n ```java\n RetentionPolicy.SOURCE //注解将被编译器丢弃 \n RetentionPolicy.CLASS //注解在class文件中可用,但会被VM丢弃 \n RetentionPolicy.RUNTIME \t\t//JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。通常自定义注解都用这个\n ```\n\n \n\n\n\n\n3. `@Documented` 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。\n\n\n\n4. `@Inherited` 允许子类继承父类中的注解。\n\n\n\n## 自定义注解\n\n一般使用元注解 \n\n- @Target,指明在哪里使用\n- @Retention, 指明在什么时候起作用\n\n例如:\n\n```java\n@Target({ElementType.METHOD, ElementType.TYPE})\n@Retention(value = RetentionPolicy.RUNTIME)\npublic@interface User{\n \n //下面是注解的参数,定义格式:“类型 参数名();” , 默认值看自己要不要写\n String name() default \"\"; //默认值为空\n int id() default -1; //默认值-1,代表不存在\n}\n```\n\n","source":"_posts/javaSE/21-注解.md","raw":"---\ntitle: 21-注解\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: '170e1590'\ndate: 2021-02-20 16:42:36\ndescription: java的注解介绍与使用(元注解、自定义注解)\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# Java注解Anotation\n\n## 什么是注解\n\n注解是jdk5.0开始引进的新技术\n\n\n\n### 作用是什么\n\n1. 注解的作用类似注释,但注释是给人(程序员)看的,而注解是给编译器看的。\n\n2. 注解通过标记包、类、字段、方法、局部变量、方法参数等元素据,告诉jvm这些元素据的信息\n\n\n\n### 格式\n\n\n\n以 `@注解名` 的格式存在,如:\n\n```java\n@override\n\n@suppressWarnings(value=\"unchecked\")\n```\n\n\n\n\n\n\n\n\n\n\n\n## 元注解\n\n\n\n元注解即用来定义注解的注解,也就是在你创建注解的时候会用到,Java有4个类型的元注解:\n\n```java\n@Target\n@Retention\n@Documented \n@Inherited\n```\n\n\n\n1. `@Target` 表示该注解用于什么地方,可以是包、类、方法......,在枚举类 `ElemenetType`中 有说明:\n\n ```java\n public enum ElementType {\n /** Class, interface (including annotation type), or enum declaration */\n TYPE,\n \n /** Field declaration (includes enum constants) */\n FIELD,\n \n /** Method declaration */\n METHOD,\n \n /** Formal parameter declaration */\n PARAMETER,\n \n /** Constructor declaration */\n CONSTRUCTOR,\n \n /** Local variable declaration */\n LOCAL_VARIABLE,\n \n /** Annotation type declaration */\n ANNOTATION_TYPE,\n \n /** Package declaration */\n PACKAGE,\n \n /**\n * Type parameter declaration\n *\n * @since 1.8\n */\n TYPE_PARAMETER,\n \n /**\n * Use of a type\n *\n * @since 1.8\n */\n TYPE_USE\n }\n ```\n\n 例如创建一个test1注解:\n\n ```java\n @Target({ElementType.METHOD, ElementType.TYPE}) //可以用在方法、class中\n @interface Test1{\n String value() default \"\"; //参数,默认为空 \n }\n ```\n\n \n\n2. `@Retention` 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:\n\n ```java\n RetentionPolicy.SOURCE //注解将被编译器丢弃 \n RetentionPolicy.CLASS //注解在class文件中可用,但会被VM丢弃 \n RetentionPolicy.RUNTIME \t\t//JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。通常自定义注解都用这个\n ```\n\n \n\n\n\n\n3. `@Documented` 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。\n\n\n\n4. `@Inherited` 允许子类继承父类中的注解。\n\n\n\n## 自定义注解\n\n一般使用元注解 \n\n- @Target,指明在哪里使用\n- @Retention, 指明在什么时候起作用\n\n例如:\n\n```java\n@Target({ElementType.METHOD, ElementType.TYPE})\n@Retention(value = RetentionPolicy.RUNTIME)\npublic@interface User{\n \n //下面是注解的参数,定义格式:“类型 参数名();” , 默认值看自己要不要写\n String name() default \"\"; //默认值为空\n int id() default -1; //默认值-1,代表不存在\n}\n```\n\n","slug":"javaSE/21-注解","published":1,"updated":"2021-08-29T15:32:14.568Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qy006rakve1z853ljh","content":"

Java注解Anotation

什么是注解

注解是jdk5.0开始引进的新技术

\n

作用是什么

    \n
  1. 注解的作用类似注释,但注释是给人(程序员)看的,而注解是给编译器看的。

    \n
  2. \n
  3. 注解通过标记包、类、字段、方法、局部变量、方法参数等元素据,告诉jvm这些元素据的信息

    \n
  4. \n
\n

格式

@注解名 的格式存在,如:

\n
1
2
3
@override

@suppressWarnings(value="unchecked")
\n

元注解

元注解即用来定义注解的注解,也就是在你创建注解的时候会用到,Java有4个类型的元注解:

\n
1
2
3
4
@Target
@Retention
@Documented
@Inherited
\n
    \n
  1. @Target 表示该注解用于什么地方,可以是包、类、方法……,在枚举类 ElemenetType中 有说明:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
    * Type parameter declaration
    *
    * @since 1.8
    */
    TYPE_PARAMETER,

    /**
    * Use of a type
    *
    * @since 1.8
    */
    TYPE_USE
    }
    \n

    例如创建一个test1注解:

    \n
    1
    2
    3
    4
    @Target({ElementType.METHOD, ElementType.TYPE})     //可以用在方法、class中
    @interface Test1{
    String value() default ""; //参数,默认为空
    }
    \n
  2. \n
\n
    \n
  1. @Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:

    \n
    1
    2
    3
    RetentionPolicy.SOURCE        //注解将被编译器丢弃 
    RetentionPolicy.CLASS //注解在class文件中可用,但会被VM丢弃
    RetentionPolicy.RUNTIME \t\t//JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。通常自定义注解都用这个
    \n
  2. \n
\n
    \n
  1. @Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。
  2. \n
\n
    \n
  1. @Inherited 允许子类继承父类中的注解。
  2. \n
\n

自定义注解

一般使用元注解

\n
    \n
  • @Target,指明在哪里使用
  • \n
  • @Retention, 指明在什么时候起作用
  • \n
\n

例如:

\n
1
2
3
4
5
6
7
8
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public@interface User{

//下面是注解的参数,定义格式:“类型 参数名();” , 默认值看自己要不要写
String name() default ""; //默认值为空
int id() default -1; //默认值-1,代表不存在
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Java注解Anotation

什么是注解

注解是jdk5.0开始引进的新技术

\n

作用是什么

    \n
  1. 注解的作用类似注释,但注释是给人(程序员)看的,而注解是给编译器看的。

    \n
  2. \n
  3. 注解通过标记包、类、字段、方法、局部变量、方法参数等元素据,告诉jvm这些元素据的信息

    \n
  4. \n
\n

格式

@注解名 的格式存在,如:

\n
1
2
3
@override

@suppressWarnings(value="unchecked")
\n

元注解

元注解即用来定义注解的注解,也就是在你创建注解的时候会用到,Java有4个类型的元注解:

\n
1
2
3
4
@Target
@Retention
@Documented
@Inherited
\n
    \n
  1. @Target 表示该注解用于什么地方,可以是包、类、方法……,在枚举类 ElemenetType中 有说明:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
    * Type parameter declaration
    *
    * @since 1.8
    */
    TYPE_PARAMETER,

    /**
    * Use of a type
    *
    * @since 1.8
    */
    TYPE_USE
    }
    \n

    例如创建一个test1注解:

    \n
    1
    2
    3
    4
    @Target({ElementType.METHOD, ElementType.TYPE})     //可以用在方法、class中
    @interface Test1{
    String value() default ""; //参数,默认为空
    }
    \n
  2. \n
\n
    \n
  1. @Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:

    \n
    1
    2
    3
    RetentionPolicy.SOURCE        //注解将被编译器丢弃 
    RetentionPolicy.CLASS //注解在class文件中可用,但会被VM丢弃
    RetentionPolicy.RUNTIME \t\t//JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。通常自定义注解都用这个
    \n
  2. \n
\n
    \n
  1. @Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。
  2. \n
\n
    \n
  1. @Inherited 允许子类继承父类中的注解。
  2. \n
\n

自定义注解

一般使用元注解

\n
    \n
  • @Target,指明在哪里使用
  • \n
  • @Retention, 指明在什么时候起作用
  • \n
\n

例如:

\n
1
2
3
4
5
6
7
8
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public@interface User{

//下面是注解的参数,定义格式:“类型 参数名();” , 默认值看自己要不要写
String name() default ""; //默认值为空
int id() default -1; //默认值-1,代表不存在
}
\n"},{"title":"3.2-数组","abbrlink":"c63fec2a","date":"2021-02-09T05:20:22.000Z","description":"Java数组的常用操作函数","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# Arrays类\n\n## 常用数组操作\n\n| 操作 | 简介 |\n| :----------: | :------------------: |\n| copyOfRange | 拷贝数组 |\n| toString | 数组转为字符串 |\n| sort() | 数组排序 |\n| binarySearch | 搜索数组中的某一元素 |\n| equals | 判断是否相同 |\n| fill | 填充 |\n\n\n\n- `copyOfRange(int[] original, int from, int to)`\n - original表示源数组\n - from表示拷贝开始下标\n - to表示结束位置下标(不包含to位置)\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n\n int[] b = Arrays.copyOfRange(a, 0, 3); //0 <= i <3\n \n for (int i = 0; i < b.length; i++) {\n System.out.print(b[i] + \" \");\n }\n \n }\n //输出:18 62 68\n}\n```\n\n- `toString(a)`\n\n 将数组a转为字符串\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n String content = Arrays.toString(a);\n System.out.println(content);\n \n }\n}\n\n// 输出:[18, 62, 68, 82, 65, 9]\n```\n\n- `binarySearch(a, b)`\n - a为数组,b为该数组中的一个元素\n - 使用binarySearch进行查找之前,必须使用sort进行排序;\n 如果数组中有多个相同的元素,查找结果是不确定的\n - 注意输出位置从1开始,而不是0\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n \n Arrays.sort(a);//用binarySearch前,必须先使用sort进行排序\n \n System.out.println(\"数字 62 出现的位置:\"+Arrays.binarySearch(a, 62));\n }\n}\n\n//输出:\n//数字 62 出现的位置:2\n\n```\n\n- `equals(a1, a2)`\n - 判断a1, a2两个数组是否相同\n - 返回值为 `boolean`值\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n int b[] = new int[] { 18, 62, 68, 82, 65, 8 };\n \n System.out.println(Arrays.equals(a, b));\n }\n}\n\n//输出:false\n```\n\n- `fill(a, b)`\n - 往数组a中填充元素b(如果a已经有值,则a中的所有值会被替换为b)\n - 无返回值\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[10];\n \n Arrays.fill(a, 5);\n \n System.out.println(Arrays.toString(a));\n \n }\n}\n\n//输出:[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\n```\n\n","source":"_posts/javaSE/3.2-数组.md","raw":"---\ntitle: 3.2-数组\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: c63fec2a\ndate: 2021-02-09 13:20:22\ndescription: Java数组的常用操作函数\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# Arrays类\n\n## 常用数组操作\n\n| 操作 | 简介 |\n| :----------: | :------------------: |\n| copyOfRange | 拷贝数组 |\n| toString | 数组转为字符串 |\n| sort() | 数组排序 |\n| binarySearch | 搜索数组中的某一元素 |\n| equals | 判断是否相同 |\n| fill | 填充 |\n\n\n\n- `copyOfRange(int[] original, int from, int to)`\n - original表示源数组\n - from表示拷贝开始下标\n - to表示结束位置下标(不包含to位置)\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n\n int[] b = Arrays.copyOfRange(a, 0, 3); //0 <= i <3\n \n for (int i = 0; i < b.length; i++) {\n System.out.print(b[i] + \" \");\n }\n \n }\n //输出:18 62 68\n}\n```\n\n- `toString(a)`\n\n 将数组a转为字符串\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n String content = Arrays.toString(a);\n System.out.println(content);\n \n }\n}\n\n// 输出:[18, 62, 68, 82, 65, 9]\n```\n\n- `binarySearch(a, b)`\n - a为数组,b为该数组中的一个元素\n - 使用binarySearch进行查找之前,必须使用sort进行排序;\n 如果数组中有多个相同的元素,查找结果是不确定的\n - 注意输出位置从1开始,而不是0\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n \n Arrays.sort(a);//用binarySearch前,必须先使用sort进行排序\n \n System.out.println(\"数字 62 出现的位置:\"+Arrays.binarySearch(a, 62));\n }\n}\n\n//输出:\n//数字 62 出现的位置:2\n\n```\n\n- `equals(a1, a2)`\n - 判断a1, a2两个数组是否相同\n - 返回值为 `boolean`值\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[] { 18, 62, 68, 82, 65, 9 };\n int b[] = new int[] { 18, 62, 68, 82, 65, 8 };\n \n System.out.println(Arrays.equals(a, b));\n }\n}\n\n//输出:false\n```\n\n- `fill(a, b)`\n - 往数组a中填充元素b(如果a已经有值,则a中的所有值会被替换为b)\n - 无返回值\n\n```java\nimport java.util.Arrays;\n \npublic class HelloWorld {\n public static void main(String[] args) {\n int a[] = new int[10];\n \n Arrays.fill(a, 5);\n \n System.out.println(Arrays.toString(a));\n \n }\n}\n\n//输出:[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]\n```\n\n","slug":"javaSE/3.2-数组","published":1,"updated":"2021-08-29T15:30:47.232Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6qz006uakve9nqxfhzr","content":"

Arrays类

常用数组操作

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作简介
copyOfRange拷贝数组
toString数组转为字符串
sort()数组排序
binarySearch搜索数组中的某一元素
equals判断是否相同
fill填充
\n
\n
    \n
  • copyOfRange(int[] original, int from, int to)
      \n
    • original表示源数组
    • \n
    • from表示拷贝开始下标
    • \n
    • to表示结束位置下标(不包含to位置)
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };

int[] b = Arrays.copyOfRange(a, 0, 3); //0 <= i <3

for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + " ");
}

}
//输出:18 62 68
}
\n
    \n
  • toString(a)

    \n

    将数组a转为字符串

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
String content = Arrays.toString(a);
System.out.println(content);

}
}

// 输出:[18, 62, 68, 82, 65, 9]
\n
    \n
  • binarySearch(a, b)
      \n
    • a为数组,b为该数组中的一个元素
    • \n
    • 使用binarySearch进行查找之前,必须使用sort进行排序;
      如果数组中有多个相同的元素,查找结果是不确定的
    • \n
    • 注意输出位置从1开始,而不是0
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };

Arrays.sort(a);//用binarySearch前,必须先使用sort进行排序

System.out.println("数字 62 出现的位置:"+Arrays.binarySearch(a, 62));
}
}

//输出:
//数字 62 出现的位置:2

\n
    \n
  • equals(a1, a2)
      \n
    • 判断a1, a2两个数组是否相同
    • \n
    • 返回值为 boolean
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
int b[] = new int[] { 18, 62, 68, 82, 65, 8 };

System.out.println(Arrays.equals(a, b));
}
}

//输出:false
\n
    \n
  • fill(a, b)
      \n
    • 往数组a中填充元素b(如果a已经有值,则a中的所有值会被替换为b)
    • \n
    • 无返回值
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[10];

Arrays.fill(a, 5);

System.out.println(Arrays.toString(a));

}
}

//输出:[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Arrays类

常用数组操作

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作简介
copyOfRange拷贝数组
toString数组转为字符串
sort()数组排序
binarySearch搜索数组中的某一元素
equals判断是否相同
fill填充
\n
\n
    \n
  • copyOfRange(int[] original, int from, int to)
      \n
    • original表示源数组
    • \n
    • from表示拷贝开始下标
    • \n
    • to表示结束位置下标(不包含to位置)
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };

int[] b = Arrays.copyOfRange(a, 0, 3); //0 <= i <3

for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + " ");
}

}
//输出:18 62 68
}
\n
    \n
  • toString(a)

    \n

    将数组a转为字符串

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
String content = Arrays.toString(a);
System.out.println(content);

}
}

// 输出:[18, 62, 68, 82, 65, 9]
\n
    \n
  • binarySearch(a, b)
      \n
    • a为数组,b为该数组中的一个元素
    • \n
    • 使用binarySearch进行查找之前,必须使用sort进行排序;
      如果数组中有多个相同的元素,查找结果是不确定的
    • \n
    • 注意输出位置从1开始,而不是0
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };

Arrays.sort(a);//用binarySearch前,必须先使用sort进行排序

System.out.println("数字 62 出现的位置:"+Arrays.binarySearch(a, 62));
}
}

//输出:
//数字 62 出现的位置:2

\n
    \n
  • equals(a1, a2)
      \n
    • 判断a1, a2两个数组是否相同
    • \n
    • 返回值为 boolean
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
int b[] = new int[] { 18, 62, 68, 82, 65, 8 };

System.out.println(Arrays.equals(a, b));
}
}

//输出:false
\n
    \n
  • fill(a, b)
      \n
    • 往数组a中填充元素b(如果a已经有值,则a中的所有值会被替换为b)
    • \n
    • 无返回值
    • \n
    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Arrays;

public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[10];

Arrays.fill(a, 5);

System.out.println(Arrays.toString(a));

}
}

//输出:[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
\n"},{"title":"3.1-数据类型","abbrlink":"52da8eea","date":"2021-02-08T10:12:41.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","description":"java的数据类型分为基本数据类型和引用数据类型,基本的有数字类型(byte、short、int、long,float、double),字符型(char),布尔型(boolen);引用数据类型有类的引用、字符串(String)","_content":"\n\n\n\n# 数据类型\n\n## 基本数据类型\n\n### 整型 (4种)\n\n- byte (8位),最高位表示正负,$-2^7$~$2^7-1$\n\n byte a = 15;\n\n- short(16位),$-2^{15}$~$2^{15}-1$\n\n short a = 567;\n\n- int(32位),$-2^{31}$~$2^{31}-1$,默认为int型\n\n - 10进制:int a = 123;\n - 16进制:int a = 0x4f;\n - 8进制:int a = 0123;\n\n- long(64位),$-2^{63}$~$2^{63}-1$\n\n long a = 678L;\n\n```java\npublic class HelloWorld{\n public static void main(String[] args){\n byte b = 1;\n short s = 200;\n int i = 300;\n long l = 400;\n \n /*如果试图给byte类型的变量赋予超出其范围的值,就会产生编译错误*/\n byte b2 = 200; //报错\n }\n}\n```\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n long val = 26L; //以L结尾的字面值表示long型\n int decVal = 26; //默认就是int型\n int hexVal = 0x1a; //16进制\n int oxVal = 032; //8进制\n int binVal = 0b11010; //2进制\n }\n}\n```\n\n\n\n### 字符型 (1种)\n\n- `char`类型用于存放一个字符,值用单引号 `''` 表示 (双引号表示字符串);\n- 长度和short一样,也是16位的,**只能存放一个字符**,超过一个字符就会产生编译错误;\n- 中文字符与英文字母一样,都占用2个字节,即16位\n\n```java\npublic class HelloWorld{\n \n public static void main(String[] args){\n char c = '中';\n char A = 65; \n \n //char 只能存放一个字符,超过一个字符就会产生编译错误\n char c2 = '中国'; //报错\n char c3 = 'ab'; //报错\n \n }\n}\n```\n\n> char与short区别:\n>\n> ​\t在java中,char和short都是两个字节的长度。但char表示的是16位无符号整数,表示的范围是0~65535。short表示的是16位有符号整数,范围为-32768~32767。char用来保存一个Unicode编码的字符。char和short之间类型转换需要强转。\n\n### 浮点型 (2种)\n\n- float(32位)\n- double(64位),小数默认都是double类型,因此要声明float类型的数,要在末尾加一个字母`f`\n\n```java\npublic class HelloWorld{\n \n public static void main(String[] args){\n \n float f = 54.321;//报错,因为54.321是double型的\n float f2 = 54.321f;\n \n double d1 = 1.234;\n double d2 = 1.2e3 //即1.2*1000\n \n }\n}\n```\n\n### 布尔型(1种)\n\n- boolen(1位):用于表示真假,`true`或 `false`,注意不能用1或0来代替\n\n```java\npublic class HelloWorld {\n \n public static void main(String[] args) {\n \n boolean b1 = true;\n boolean b2 = false;\n \n // 虽然布尔型真正存放的数据是0(false) 1(true)\n // 但是,不能直接使用0 1 进行赋值\n boolean b3 = 1;\n \n }\n}\n```\n\n\n\n## 引用数据类型\n\n引用数据类型存储的是数据存放的地址\n\n### 类\n\n在Java中,可以创建一个类,再利用类来创建一个新的实例(对象),该实例的类型就是一种引用数据类型\n\n```java\npublic class Date{\n int year;\n int month;\n int day;\n}\n\n```\n\n```java\nDate d1 = new Date() //d1为引用数据类型\n```\n\n### 字符串(String)\n\nString将字符串当作一个整体来处理,不能修改串中的字符元素,可看作一个字符串常量。\n\n如`\"This is a string.\\n\"`。注意使用双引号。\n\n- 定义一个String:\n\n```java\nString s1 = new String(\"123abc\");\n\nString s2;\ns2 = \"123abc\";\n\nString s3 = \"123abc\";\n```\n\n- 连接两个字符串用 `+`\n\n```java\nString s1 = \"123\";\nString s2 = \"abc\";\nString s3;\ns3 = s1 + s2;\n```\n\n### 数组\n\n数组是一个**固定长度**的,包含了**相同类型**数据的 **容器**\n\n- #### 声明数组(不会创建数组)\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n // 声明一个数组\n int[] a;\n }\n}\n\n/*\nint[] a; 声明了一个数组变量。\n[]表示该变量是一个数组\nint 表示数组里的每一个元素都是一个整数\na 是变量名\n但是,仅仅是这一句声明,不会创建数组\n*/\n```\n\n- #### 创建数组\n\n```java\n//先声明再创建\nint[] a;\na = new int[5]\n```\n\n```java\n//声明的同时,创建一个数组\nint[] a = new int[5]; \n```\n\n数组名 `a`是一个引用\n\n- #### 初始化数组\n\n 如果数组的类型为int,未初始化时每个元素默认为0\n\n - 先声明,创建数组,再一个个元素进行赋值\n \n ```java\n int[] a = new int[5]; //声明和创建\n \n //没有赋值,那么就会使用默认值\n //作为int类型的数组,默认值是0\n System.out.println(a[0]);\n \n //进行赋值\n \n a[0] = 100;\n a[1] = 101;\n a[2] = 103;\n a[3] = 120;\n a[4] = 140;\n ```\n \n - 声明、创建的同时初始化\n \n ```java\n public class HelloWorld {\n public static void main(String[] args) {\n //写法一: 分配空间同时赋值\n int[] a = new int[]{100,102,444,836,3236};\n \n //写法二: 省略了new int[],效果一样\n int[] b = {100,102,444,836,3236};\n \n }\n }\n ```\n \n 注意:初始化的同时不能指定数组长度:\n \n ```java\n int[] c = new int[5]{100,102,444,836,3236};\n ```\n \n \n\n- #### 访问数组\n\n```java\na[0]=1;\na[1]=2;\na[2]=3;\n```\n\n- #### 数组长度\n\n `.length`属性用于访问一个数组的长度\n\n```java\nSystem.out.println(a.length)\n```\n\n- #### 二维数组\n\n 下面声明、创建并初始化了一个二维数组\n\n```java\nint[][] b = new int[][]{\n {1,2,3},\n {4,5,6},\n {7,8,9}\n};\n```\n\n","source":"_posts/javaSE/3.1-数据类型.md","raw":"---\ntitle: 3.1-数据类型\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 52da8eea\ndate: 2021-02-08 18:12:41\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\ndescription: java的数据类型分为基本数据类型和引用数据类型,基本的有数字类型(byte、short、int、long,float、double),字符型(char),布尔型(boolen);引用数据类型有类的引用、字符串(String)\n---\n\n\n\n\n# 数据类型\n\n## 基本数据类型\n\n### 整型 (4种)\n\n- byte (8位),最高位表示正负,$-2^7$~$2^7-1$\n\n byte a = 15;\n\n- short(16位),$-2^{15}$~$2^{15}-1$\n\n short a = 567;\n\n- int(32位),$-2^{31}$~$2^{31}-1$,默认为int型\n\n - 10进制:int a = 123;\n - 16进制:int a = 0x4f;\n - 8进制:int a = 0123;\n\n- long(64位),$-2^{63}$~$2^{63}-1$\n\n long a = 678L;\n\n```java\npublic class HelloWorld{\n public static void main(String[] args){\n byte b = 1;\n short s = 200;\n int i = 300;\n long l = 400;\n \n /*如果试图给byte类型的变量赋予超出其范围的值,就会产生编译错误*/\n byte b2 = 200; //报错\n }\n}\n```\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n long val = 26L; //以L结尾的字面值表示long型\n int decVal = 26; //默认就是int型\n int hexVal = 0x1a; //16进制\n int oxVal = 032; //8进制\n int binVal = 0b11010; //2进制\n }\n}\n```\n\n\n\n### 字符型 (1种)\n\n- `char`类型用于存放一个字符,值用单引号 `''` 表示 (双引号表示字符串);\n- 长度和short一样,也是16位的,**只能存放一个字符**,超过一个字符就会产生编译错误;\n- 中文字符与英文字母一样,都占用2个字节,即16位\n\n```java\npublic class HelloWorld{\n \n public static void main(String[] args){\n char c = '中';\n char A = 65; \n \n //char 只能存放一个字符,超过一个字符就会产生编译错误\n char c2 = '中国'; //报错\n char c3 = 'ab'; //报错\n \n }\n}\n```\n\n> char与short区别:\n>\n> ​\t在java中,char和short都是两个字节的长度。但char表示的是16位无符号整数,表示的范围是0~65535。short表示的是16位有符号整数,范围为-32768~32767。char用来保存一个Unicode编码的字符。char和short之间类型转换需要强转。\n\n### 浮点型 (2种)\n\n- float(32位)\n- double(64位),小数默认都是double类型,因此要声明float类型的数,要在末尾加一个字母`f`\n\n```java\npublic class HelloWorld{\n \n public static void main(String[] args){\n \n float f = 54.321;//报错,因为54.321是double型的\n float f2 = 54.321f;\n \n double d1 = 1.234;\n double d2 = 1.2e3 //即1.2*1000\n \n }\n}\n```\n\n### 布尔型(1种)\n\n- boolen(1位):用于表示真假,`true`或 `false`,注意不能用1或0来代替\n\n```java\npublic class HelloWorld {\n \n public static void main(String[] args) {\n \n boolean b1 = true;\n boolean b2 = false;\n \n // 虽然布尔型真正存放的数据是0(false) 1(true)\n // 但是,不能直接使用0 1 进行赋值\n boolean b3 = 1;\n \n }\n}\n```\n\n\n\n## 引用数据类型\n\n引用数据类型存储的是数据存放的地址\n\n### 类\n\n在Java中,可以创建一个类,再利用类来创建一个新的实例(对象),该实例的类型就是一种引用数据类型\n\n```java\npublic class Date{\n int year;\n int month;\n int day;\n}\n\n```\n\n```java\nDate d1 = new Date() //d1为引用数据类型\n```\n\n### 字符串(String)\n\nString将字符串当作一个整体来处理,不能修改串中的字符元素,可看作一个字符串常量。\n\n如`\"This is a string.\\n\"`。注意使用双引号。\n\n- 定义一个String:\n\n```java\nString s1 = new String(\"123abc\");\n\nString s2;\ns2 = \"123abc\";\n\nString s3 = \"123abc\";\n```\n\n- 连接两个字符串用 `+`\n\n```java\nString s1 = \"123\";\nString s2 = \"abc\";\nString s3;\ns3 = s1 + s2;\n```\n\n### 数组\n\n数组是一个**固定长度**的,包含了**相同类型**数据的 **容器**\n\n- #### 声明数组(不会创建数组)\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n // 声明一个数组\n int[] a;\n }\n}\n\n/*\nint[] a; 声明了一个数组变量。\n[]表示该变量是一个数组\nint 表示数组里的每一个元素都是一个整数\na 是变量名\n但是,仅仅是这一句声明,不会创建数组\n*/\n```\n\n- #### 创建数组\n\n```java\n//先声明再创建\nint[] a;\na = new int[5]\n```\n\n```java\n//声明的同时,创建一个数组\nint[] a = new int[5]; \n```\n\n数组名 `a`是一个引用\n\n- #### 初始化数组\n\n 如果数组的类型为int,未初始化时每个元素默认为0\n\n - 先声明,创建数组,再一个个元素进行赋值\n \n ```java\n int[] a = new int[5]; //声明和创建\n \n //没有赋值,那么就会使用默认值\n //作为int类型的数组,默认值是0\n System.out.println(a[0]);\n \n //进行赋值\n \n a[0] = 100;\n a[1] = 101;\n a[2] = 103;\n a[3] = 120;\n a[4] = 140;\n ```\n \n - 声明、创建的同时初始化\n \n ```java\n public class HelloWorld {\n public static void main(String[] args) {\n //写法一: 分配空间同时赋值\n int[] a = new int[]{100,102,444,836,3236};\n \n //写法二: 省略了new int[],效果一样\n int[] b = {100,102,444,836,3236};\n \n }\n }\n ```\n \n 注意:初始化的同时不能指定数组长度:\n \n ```java\n int[] c = new int[5]{100,102,444,836,3236};\n ```\n \n \n\n- #### 访问数组\n\n```java\na[0]=1;\na[1]=2;\na[2]=3;\n```\n\n- #### 数组长度\n\n `.length`属性用于访问一个数组的长度\n\n```java\nSystem.out.println(a.length)\n```\n\n- #### 二维数组\n\n 下面声明、创建并初始化了一个二维数组\n\n```java\nint[][] b = new int[][]{\n {1,2,3},\n {4,5,6},\n {7,8,9}\n};\n```\n\n","slug":"javaSE/3.1-数据类型","published":1,"updated":"2021-08-29T15:30:43.790Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r0006xakve9680bvvn","content":"

数据类型

基本数据类型

整型 (4种)

    \n
  • byte (8位),最高位表示正负,$-2^7$~$2^7-1$

    \n

    byte a = 15;

    \n
  • \n
  • short(16位),$-2^{15}$~$2^{15}-1$

    \n

    short a = 567;

    \n
  • \n
  • int(32位),$-2^{31}$~$2^{31}-1$,默认为int型

    \n
      \n
    • 10进制:int a = 123;
    • \n
    • 16进制:int a = 0x4f;
    • \n
    • 8进制:int a = 0123;
    • \n
    \n
  • \n
  • long(64位),$-2^{63}$~$2^{63}-1$

    \n

    long a = 678L;

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
public class HelloWorld{
public static void main(String[] args){
byte b = 1;
short s = 200;
int i = 300;
long l = 400;

/*如果试图给byte类型的变量赋予超出其范围的值,就会产生编译错误*/
byte b2 = 200; //报错
}
}
\n
1
2
3
4
5
6
7
8
9
public class HelloWorld {
public static void main(String[] args) {
long val = 26L; //以L结尾的字面值表示long型
int decVal = 26; //默认就是int型
int hexVal = 0x1a; //16进制
int oxVal = 032; //8进制
int binVal = 0b11010; //2进制
}
}
\n

字符型 (1种)

    \n
  • char类型用于存放一个字符,值用单引号 '' 表示 (双引号表示字符串);
  • \n
  • 长度和short一样,也是16位的,只能存放一个字符,超过一个字符就会产生编译错误;
  • \n
  • 中文字符与英文字母一样,都占用2个字节,即16位
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorld{

public static void main(String[] args){
char c = '中';
char A = 65;

//char 只能存放一个字符,超过一个字符就会产生编译错误
char c2 = '中国'; //报错
char c3 = 'ab'; //报错

}
}
\n
\n

char与short区别:

\n

​ 在java中,char和short都是两个字节的长度。但char表示的是16位无符号整数,表示的范围是0~65535。short表示的是16位有符号整数,范围为-32768~32767。char用来保存一个Unicode编码的字符。char和short之间类型转换需要强转。

\n
\n

浮点型 (2种)

    \n
  • float(32位)
  • \n
  • double(64位),小数默认都是double类型,因此要声明float类型的数,要在末尾加一个字母f
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorld{

public static void main(String[] args){

float f = 54.321;//报错,因为54.321是double型的
float f2 = 54.321f;

double d1 = 1.234;
double d2 = 1.2e3 //即1.2*1000

}
}
\n

布尔型(1种)

    \n
  • boolen(1位):用于表示真假,truefalse,注意不能用1或0来代替
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloWorld {

public static void main(String[] args) {

boolean b1 = true;
boolean b2 = false;

// 虽然布尔型真正存放的数据是0(false) 1(true)
// 但是,不能直接使用0 1 进行赋值
boolean b3 = 1;

}
}
\n

引用数据类型

引用数据类型存储的是数据存放的地址

\n

在Java中,可以创建一个类,再利用类来创建一个新的实例(对象),该实例的类型就是一种引用数据类型

\n
1
2
3
4
5
6
public class Date{
int year;
int month;
int day;
}

\n
1
Date d1 = new Date() //d1为引用数据类型
\n

字符串(String)

String将字符串当作一个整体来处理,不能修改串中的字符元素,可看作一个字符串常量。

\n

"This is a string.\\n"。注意使用双引号。

\n
    \n
  • 定义一个String:
  • \n
\n
1
2
3
4
5
6
String s1 = new String("123abc");

String s2;
s2 = "123abc";

String s3 = "123abc";
\n
    \n
  • 连接两个字符串用 +
  • \n
\n
1
2
3
4
String s1 = "123";
String s2 = "abc";
String s3;
s3 = s1 + s2;
\n

数组

数组是一个固定长度的,包含了相同类型数据的 容器

\n
    \n
  • 声明数组(不会创建数组)

  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class HelloWorld {
public static void main(String[] args) {
// 声明一个数组
int[] a;
}
}

/*
int[] a; 声明了一个数组变量。
[]表示该变量是一个数组
int 表示数组里的每一个元素都是一个整数
a 是变量名
但是,仅仅是这一句声明,不会创建数组
*/
\n
    \n
  • 创建数组

  • \n
\n
1
2
3
//先声明再创建
int[] a;
a = new int[5]
\n
1
2
//声明的同时,创建一个数组
int[] a = new int[5];
\n

数组名 a是一个引用

\n
    \n
  • 初始化数组

    如果数组的类型为int,未初始化时每个元素默认为0

    \n
      \n
    • 先声明,创建数组,再一个个元素进行赋值
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int[] a = new int[5]; //声明和创建

    //没有赋值,那么就会使用默认值
    //作为int类型的数组,默认值是0
    System.out.println(a[0]);

    //进行赋值

    a[0] = 100;
    a[1] = 101;
    a[2] = 103;
    a[3] = 120;
    a[4] = 140;
    \n
      \n
    • 声明、创建的同时初始化
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloWorld {
    public static void main(String[] args) {
    //写法一: 分配空间同时赋值
    int[] a = new int[]{100,102,444,836,3236};

    //写法二: 省略了new int[],效果一样
    int[] b = {100,102,444,836,3236};

    }
    }
    \n

    注意:初始化的同时不能指定数组长度:

    \n
    1
    int[] c = new int[5]{100,102,444,836,3236};
    \n
  • \n
\n
    \n
  • 访问数组

  • \n
\n
1
2
3
a[0]=1;
a[1]=2;
a[2]=3;
\n
    \n
  • 数组长度

    .length属性用于访问一个数组的长度

    \n
  • \n
\n
1
System.out.println(a.length)
\n
    \n
  • 二维数组

    下面声明、创建并初始化了一个二维数组

    \n
  • \n
\n
1
2
3
4
5
int[][] b = new int[][]{
{1,2,3},
{4,5,6},
{7,8,9}
};
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

数据类型

基本数据类型

整型 (4种)

    \n
  • byte (8位),最高位表示正负,$-2^7$~$2^7-1$

    \n

    byte a = 15;

    \n
  • \n
  • short(16位),$-2^{15}$~$2^{15}-1$

    \n

    short a = 567;

    \n
  • \n
  • int(32位),$-2^{31}$~$2^{31}-1$,默认为int型

    \n
      \n
    • 10进制:int a = 123;
    • \n
    • 16进制:int a = 0x4f;
    • \n
    • 8进制:int a = 0123;
    • \n
    \n
  • \n
  • long(64位),$-2^{63}$~$2^{63}-1$

    \n

    long a = 678L;

    \n
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
public class HelloWorld{
public static void main(String[] args){
byte b = 1;
short s = 200;
int i = 300;
long l = 400;

/*如果试图给byte类型的变量赋予超出其范围的值,就会产生编译错误*/
byte b2 = 200; //报错
}
}
\n
1
2
3
4
5
6
7
8
9
public class HelloWorld {
public static void main(String[] args) {
long val = 26L; //以L结尾的字面值表示long型
int decVal = 26; //默认就是int型
int hexVal = 0x1a; //16进制
int oxVal = 032; //8进制
int binVal = 0b11010; //2进制
}
}
\n

字符型 (1种)

    \n
  • char类型用于存放一个字符,值用单引号 '' 表示 (双引号表示字符串);
  • \n
  • 长度和short一样,也是16位的,只能存放一个字符,超过一个字符就会产生编译错误;
  • \n
  • 中文字符与英文字母一样,都占用2个字节,即16位
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorld{

public static void main(String[] args){
char c = '中';
char A = 65;

//char 只能存放一个字符,超过一个字符就会产生编译错误
char c2 = '中国'; //报错
char c3 = 'ab'; //报错

}
}
\n
\n

char与short区别:

\n

​ 在java中,char和short都是两个字节的长度。但char表示的是16位无符号整数,表示的范围是0~65535。short表示的是16位有符号整数,范围为-32768~32767。char用来保存一个Unicode编码的字符。char和short之间类型转换需要强转。

\n
\n

浮点型 (2种)

    \n
  • float(32位)
  • \n
  • double(64位),小数默认都是double类型,因此要声明float类型的数,要在末尾加一个字母f
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
public class HelloWorld{

public static void main(String[] args){

float f = 54.321;//报错,因为54.321是double型的
float f2 = 54.321f;

double d1 = 1.234;
double d2 = 1.2e3 //即1.2*1000

}
}
\n

布尔型(1种)

    \n
  • boolen(1位):用于表示真假,truefalse,注意不能用1或0来代替
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloWorld {

public static void main(String[] args) {

boolean b1 = true;
boolean b2 = false;

// 虽然布尔型真正存放的数据是0(false) 1(true)
// 但是,不能直接使用0 1 进行赋值
boolean b3 = 1;

}
}
\n

引用数据类型

引用数据类型存储的是数据存放的地址

\n

在Java中,可以创建一个类,再利用类来创建一个新的实例(对象),该实例的类型就是一种引用数据类型

\n
1
2
3
4
5
6
public class Date{
int year;
int month;
int day;
}

\n
1
Date d1 = new Date() //d1为引用数据类型
\n

字符串(String)

String将字符串当作一个整体来处理,不能修改串中的字符元素,可看作一个字符串常量。

\n

"This is a string.\\n"。注意使用双引号。

\n
    \n
  • 定义一个String:
  • \n
\n
1
2
3
4
5
6
String s1 = new String("123abc");

String s2;
s2 = "123abc";

String s3 = "123abc";
\n
    \n
  • 连接两个字符串用 +
  • \n
\n
1
2
3
4
String s1 = "123";
String s2 = "abc";
String s3;
s3 = s1 + s2;
\n

数组

数组是一个固定长度的,包含了相同类型数据的 容器

\n
    \n
  • 声明数组(不会创建数组)

  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class HelloWorld {
public static void main(String[] args) {
// 声明一个数组
int[] a;
}
}

/*
int[] a; 声明了一个数组变量。
[]表示该变量是一个数组
int 表示数组里的每一个元素都是一个整数
a 是变量名
但是,仅仅是这一句声明,不会创建数组
*/
\n
    \n
  • 创建数组

  • \n
\n
1
2
3
//先声明再创建
int[] a;
a = new int[5]
\n
1
2
//声明的同时,创建一个数组
int[] a = new int[5];
\n

数组名 a是一个引用

\n
    \n
  • 初始化数组

    如果数组的类型为int,未初始化时每个元素默认为0

    \n
      \n
    • 先声明,创建数组,再一个个元素进行赋值
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int[] a = new int[5]; //声明和创建

    //没有赋值,那么就会使用默认值
    //作为int类型的数组,默认值是0
    System.out.println(a[0]);

    //进行赋值

    a[0] = 100;
    a[1] = 101;
    a[2] = 103;
    a[3] = 120;
    a[4] = 140;
    \n
      \n
    • 声明、创建的同时初始化
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloWorld {
    public static void main(String[] args) {
    //写法一: 分配空间同时赋值
    int[] a = new int[]{100,102,444,836,3236};

    //写法二: 省略了new int[],效果一样
    int[] b = {100,102,444,836,3236};

    }
    }
    \n

    注意:初始化的同时不能指定数组长度:

    \n
    1
    int[] c = new int[5]{100,102,444,836,3236};
    \n
  • \n
\n
    \n
  • 访问数组

  • \n
\n
1
2
3
a[0]=1;
a[1]=2;
a[2]=3;
\n
    \n
  • 数组长度

    .length属性用于访问一个数组的长度

    \n
  • \n
\n
1
System.out.println(a.length)
\n
    \n
  • 二维数组

    下面声明、创建并初始化了一个二维数组

    \n
  • \n
\n
1
2
3
4
5
int[][] b = new int[][]{
{1,2,3},
{4,5,6},
{7,8,9}
};
\n"},{"title":"5-操作符","abbrlink":"8a45e6a3","date":"2021-02-11T08:41:16.000Z","description":"java的运算符,基本运算、关系运算、逻辑运算、位运算","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 操作符\n\n## 算数操作符\n\n### 基本操作符:\n\n```java\n+ - * / %\n```\n\n\n\n### 5.1.2 任意运算单元的长度超过int\n\n如果有任何运算单元的长度超过int,那么运算结果就按照最长的长度计算\n比如:\n\n```java\nint a = 5;\nlong b = 6;\n// a+b 结果类型是long\n```\n\n### 任意运算单元的长度小于int\n\n如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算\n\n如:byte + byte -> int, short + short -> int\n\n\n\n### 自增、自减\n\n```java\n//以++为例\nint i = 5;\nint s;\ns = i++; //先取值,再运算,结果s的值即表达式的值,为5,i的值为6\n\n```\n\n```java\n以++为例\nint i = 5;\nint s;\ns = ++i; //先运算,再取值,结果s的值即表达式的值,为6,i的值为6\n\n```\n\n\n\n## 关系操作符\n\n比较两个变量之间关系,返回`true或false`\n\n```\n>\n<\n== 等于\n>=\n<=\n!= 不等于\n```\n\n\n\n## 逻辑操作符\n\n| 操作符 | 简称 | 意义 |\n| :----: | :----: | :---------------------------------------------------------: |\n| & | 长路与 | 无论第一个表达式的值是true或者false,第二个的值,都会被运算 |\n| && | 短路与 | 只要第一个表达式的值是false的,第二个表达式就不进行运算了 |\n| \\| | 长路或 | 符号两侧的表达式都被计算 |\n| \\|\\| | 短路或 | 只要第一个是true的,第二个就不进行运算了 |\n| ! | 取反 | 真的将变成假,假的将变成真 |\n| ^ | 异或 | 符号两边一真一假,返回真;两边同为真或同为假,返回假 |\n\n---\n| 操作符 | 同 | 异 |\n| :------: | :----------------------------: | :--------------------: |\n| &、&& | 运算符两边都为真时,结果才为真 | 第二个表达式是否被计算 |\n| \\|、\\|\\| | 运算符两边都为假时,结果才为假 | |\n\n\n\n\n\n## 位运算符\n\n| 运算符 | 简介 |\n| :----------------------: | :------------------: |\n| Integer.toBinaryString() | 一个整数的二进制表达 |\n| `|` | 位或 |\n| `&` | 位与 |\n| `^` | 异或 |\n| `~` | 取反 |\n| `<<` | 左移,即乘以$2^n$ |\n| `>>` | 右移 |\n\n\n\n## 三元操作符\n\n条件运算符`?:`是一个三元操作符\n\n用法:\n\n```java\n表达式?值1:值2\n如果表达式为真,返回值1\n如果表达式为假,返回值2\n```\n\n","source":"_posts/javaSE/5-操作符.md","raw":"---\ntitle: 5-操作符\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 8a45e6a3\ndate: 2021-02-11 16:41:16\ndescription: java的运算符,基本运算、关系运算、逻辑运算、位运算\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 操作符\n\n## 算数操作符\n\n### 基本操作符:\n\n```java\n+ - * / %\n```\n\n\n\n### 5.1.2 任意运算单元的长度超过int\n\n如果有任何运算单元的长度超过int,那么运算结果就按照最长的长度计算\n比如:\n\n```java\nint a = 5;\nlong b = 6;\n// a+b 结果类型是long\n```\n\n### 任意运算单元的长度小于int\n\n如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算\n\n如:byte + byte -> int, short + short -> int\n\n\n\n### 自增、自减\n\n```java\n//以++为例\nint i = 5;\nint s;\ns = i++; //先取值,再运算,结果s的值即表达式的值,为5,i的值为6\n\n```\n\n```java\n以++为例\nint i = 5;\nint s;\ns = ++i; //先运算,再取值,结果s的值即表达式的值,为6,i的值为6\n\n```\n\n\n\n## 关系操作符\n\n比较两个变量之间关系,返回`true或false`\n\n```\n>\n<\n== 等于\n>=\n<=\n!= 不等于\n```\n\n\n\n## 逻辑操作符\n\n| 操作符 | 简称 | 意义 |\n| :----: | :----: | :---------------------------------------------------------: |\n| & | 长路与 | 无论第一个表达式的值是true或者false,第二个的值,都会被运算 |\n| && | 短路与 | 只要第一个表达式的值是false的,第二个表达式就不进行运算了 |\n| \\| | 长路或 | 符号两侧的表达式都被计算 |\n| \\|\\| | 短路或 | 只要第一个是true的,第二个就不进行运算了 |\n| ! | 取反 | 真的将变成假,假的将变成真 |\n| ^ | 异或 | 符号两边一真一假,返回真;两边同为真或同为假,返回假 |\n\n---\n| 操作符 | 同 | 异 |\n| :------: | :----------------------------: | :--------------------: |\n| &、&& | 运算符两边都为真时,结果才为真 | 第二个表达式是否被计算 |\n| \\|、\\|\\| | 运算符两边都为假时,结果才为假 | |\n\n\n\n\n\n## 位运算符\n\n| 运算符 | 简介 |\n| :----------------------: | :------------------: |\n| Integer.toBinaryString() | 一个整数的二进制表达 |\n| `|` | 位或 |\n| `&` | 位与 |\n| `^` | 异或 |\n| `~` | 取反 |\n| `<<` | 左移,即乘以$2^n$ |\n| `>>` | 右移 |\n\n\n\n## 三元操作符\n\n条件运算符`?:`是一个三元操作符\n\n用法:\n\n```java\n表达式?值1:值2\n如果表达式为真,返回值1\n如果表达式为假,返回值2\n```\n\n","slug":"javaSE/5-操作符","published":1,"updated":"2021-08-29T15:30:53.821Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r10070akvedu2zcjwq","content":"

操作符

算数操作符

基本操作符:

1
+ - * / %
\n

5.1.2 任意运算单元的长度超过int

如果有任何运算单元的长度超过int,那么运算结果就按照最长的长度计算
比如:

\n
1
2
3
int a = 5;
long b = 6;
// a+b 结果类型是long
\n

任意运算单元的长度小于int

如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算

\n

如:byte + byte -> int, short + short -> int

\n

自增、自减

1
2
3
4
5
//以++为例
int i = 5;
int s;
s = i++; //先取值,再运算,结果s的值即表达式的值,为5,i的值为6

\n
1
2
3
4
5
以++为例
int i = 5;
int s;
s = ++i; //先运算,再取值,结果s的值即表达式的值,为6,i的值为6

\n

关系操作符

比较两个变量之间关系,返回true或false

\n
1
2
3
4
5
6
>
<
== 等于
>=
<=
!= 不等于
\n

逻辑操作符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作符简称意义
&长路与无论第一个表达式的值是true或者false,第二个的值,都会被运算
&&短路与只要第一个表达式的值是false的,第二个表达式就不进行运算了
\\长路或符号两侧的表达式都被计算
\\\\短路或只要第一个是true的,第二个就不进行运算了
!取反真的将变成假,假的将变成真
^异或符号两边一真一假,返回真;两边同为真或同为假,返回假
\n
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作符
&、&&运算符两边都为真时,结果才为真第二个表达式是否被计算
\\、\\\\运算符两边都为假时,结果才为假
\n
\n

位运算符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符简介
Integer.toBinaryString()一个整数的二进制表达
``位或
&位与
^异或
~取反
<<左移,即乘以$2^n$
>>右移
\n
\n

三元操作符

条件运算符?:是一个三元操作符

\n

用法:

\n
1
2
3
表达式?值1:值2
如果表达式为真,返回值1
如果表达式为假,返回值2
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

操作符

算数操作符

基本操作符:

1
+ - * / %
\n

5.1.2 任意运算单元的长度超过int

如果有任何运算单元的长度超过int,那么运算结果就按照最长的长度计算
比如:

\n
1
2
3
int a = 5;
long b = 6;
// a+b 结果类型是long
\n

任意运算单元的长度小于int

如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算

\n

如:byte + byte -> int, short + short -> int

\n

自增、自减

1
2
3
4
5
//以++为例
int i = 5;
int s;
s = i++; //先取值,再运算,结果s的值即表达式的值,为5,i的值为6

\n
1
2
3
4
5
以++为例
int i = 5;
int s;
s = ++i; //先运算,再取值,结果s的值即表达式的值,为6,i的值为6

\n

关系操作符

比较两个变量之间关系,返回true或false

\n
1
2
3
4
5
6
>
<
== 等于
>=
<=
!= 不等于
\n

逻辑操作符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作符简称意义
&长路与无论第一个表达式的值是true或者false,第二个的值,都会被运算
&&短路与只要第一个表达式的值是false的,第二个表达式就不进行运算了
\\长路或符号两侧的表达式都被计算
\\\\短路或只要第一个是true的,第二个就不进行运算了
!取反真的将变成假,假的将变成真
^异或符号两边一真一假,返回真;两边同为真或同为假,返回假
\n
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
操作符
&、&&运算符两边都为真时,结果才为真第二个表达式是否被计算
\\、\\\\运算符两边都为假时,结果才为假
\n
\n

位运算符

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
运算符简介
Integer.toBinaryString()一个整数的二进制表达
``位或
&位与
^异或
~取反
<<左移,即乘以$2^n$
>>右移
\n
\n

三元操作符

条件运算符?:是一个三元操作符

\n

用法:

\n
1
2
3
表达式?值1:值2
如果表达式为真,返回值1
如果表达式为假,返回值2
\n"},{"title":"4-变量","abbrlink":"68f2c599","date":"2021-02-10T06:55:37.000Z","description":"Java变量命名、类型转换、分类","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 变量\n\n## 命名规则\n\n- 变量名只能使用 `字母`、`数字`、`$`、`_`\n- 变量第一个字符 只能使用 `字母 、$、 _`\n- 使用完整单词命名\n\n\n\n## 类型转换\n\n- 低精度向高精度转换可以正常转换\n\n- 高精度向低精度转换有风险\n\n ```java\n public class HelloWorld {\n \n public static void main(String[] args) {\n \n byte b = 5;\n int i1 = 10;\n int i2 = 300;\n \n b = (byte) i1;\n //因为i1的值是在byte范围之内,所以即便进行强制转换\n //最后得到的值,也是10\n System.out.println(b);\n \n //因为i2的值是在byte范围之外,所以就会按照byte的长度进行截取\n //i2的值是300,其对应的二进制数是 100101100\n //按照byte的长度8位进行截取后,其值为 00101100 即44\n b =(byte) i2;\n System.out.println(b);\n \n //查看一个整数对应的二进制的方法:\n System.out.println(Integer.toBinaryString(i2));\n \n }\n }\n ```\n\n 注意:`short + short = int`\n\n## 变量分类\n\n- 成员变量(字段、属性、Field):\n\n 当一个变量被声明在类下面,变量就叫做**成员变量**,或**字段** 、**属性**、**Field**\n\n 比如变量下面例子中,`i`就是一个属性。那么从第2行这个变量声明的位置开始,整个类都可以访问得到;所以其作用域就是从其声明的位置开始的整个类\n\n ```java\n public class HelloWorld {\n int i = 1;\n int j = i; //其他的属性可以访问i\n public void method1(){\n System.out.println(i); //方法1里可以访问i\n }\n public void method2(){\n System.out.println(i); //方法2里可以访问i\n }\n }\n ```\n\n- 参数\n\n 如果一个变量,是声明在一个方法上的,就叫做**参数**,参数的作用域即为**该方法**内的所有代码,在该方法外面不能使用该参数\n\n ```java\n public class HelloWorld {\n \n public void method1(int i){ //参数i的作用域即方法method1\n System.out.println(i);\n }\n public void method2(){\n \n System.out.println(i); //method2 不能访问参数i\n }\n \n int j = i; //类里面也不能访问参数i\n }\n ```\n\n- 局部变量\n\n 声明在方法内的变量,叫做局部变量,其作用域在声明开始的位置,到其所处于的块结束位置。\n\n ```java\n public class HelloWorld {\n \n public void method1() {\n int i = 5; //其作用范围是从声明的第4行,到其所处于的块结束12行位置\n System.out.println(i);\n { //子块\n System.out.println(i); //可以访问i\n int j = 6;\n System.out.println(j); //可以访问j\n }\n System.out.println(j); //不能访问j,因为其作用域到第10行就结束了\n }\n \n }\n ```\n\n\n\n- 静态变量\n\n ```java\n static int age;\t//(类变量)\n ```\n\n \n\n## final变量\n\n用final修饰的变量,只能赋值1次,因此常**用于定义常量**\n\n```java\npublic class HelloWorld {\n \n public void method1() {\n final int i = 5;\n \n i = 10; //i在第4行已经被赋值过了,所以这里会出现编译错误\n \n }\n \n}\n```\n\n但是对于引用类型的数据,变量存储的是地址,地址不可变,但地址里存放的值可以改变\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n final int []a = {1,2,3};\n a[1] = 10;\n a[2] = 100;\n \n System.out.println(a[1]);\n System.out.println(a[2]);\n \n // 注意这样写:a = {4,5,6}; 是错误的\n\n }\n}\n```\n\n输出如下![image-20210122203958650](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210122203958650.png)\n\n\n\n\n\n\n\n","source":"_posts/javaSE/4-变量.md","raw":"---\ntitle: 4-变量\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 68f2c599\ndate: 2021-02-10 14:55:37\ndescription: Java变量命名、类型转换、分类\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 变量\n\n## 命名规则\n\n- 变量名只能使用 `字母`、`数字`、`$`、`_`\n- 变量第一个字符 只能使用 `字母 、$、 _`\n- 使用完整单词命名\n\n\n\n## 类型转换\n\n- 低精度向高精度转换可以正常转换\n\n- 高精度向低精度转换有风险\n\n ```java\n public class HelloWorld {\n \n public static void main(String[] args) {\n \n byte b = 5;\n int i1 = 10;\n int i2 = 300;\n \n b = (byte) i1;\n //因为i1的值是在byte范围之内,所以即便进行强制转换\n //最后得到的值,也是10\n System.out.println(b);\n \n //因为i2的值是在byte范围之外,所以就会按照byte的长度进行截取\n //i2的值是300,其对应的二进制数是 100101100\n //按照byte的长度8位进行截取后,其值为 00101100 即44\n b =(byte) i2;\n System.out.println(b);\n \n //查看一个整数对应的二进制的方法:\n System.out.println(Integer.toBinaryString(i2));\n \n }\n }\n ```\n\n 注意:`short + short = int`\n\n## 变量分类\n\n- 成员变量(字段、属性、Field):\n\n 当一个变量被声明在类下面,变量就叫做**成员变量**,或**字段** 、**属性**、**Field**\n\n 比如变量下面例子中,`i`就是一个属性。那么从第2行这个变量声明的位置开始,整个类都可以访问得到;所以其作用域就是从其声明的位置开始的整个类\n\n ```java\n public class HelloWorld {\n int i = 1;\n int j = i; //其他的属性可以访问i\n public void method1(){\n System.out.println(i); //方法1里可以访问i\n }\n public void method2(){\n System.out.println(i); //方法2里可以访问i\n }\n }\n ```\n\n- 参数\n\n 如果一个变量,是声明在一个方法上的,就叫做**参数**,参数的作用域即为**该方法**内的所有代码,在该方法外面不能使用该参数\n\n ```java\n public class HelloWorld {\n \n public void method1(int i){ //参数i的作用域即方法method1\n System.out.println(i);\n }\n public void method2(){\n \n System.out.println(i); //method2 不能访问参数i\n }\n \n int j = i; //类里面也不能访问参数i\n }\n ```\n\n- 局部变量\n\n 声明在方法内的变量,叫做局部变量,其作用域在声明开始的位置,到其所处于的块结束位置。\n\n ```java\n public class HelloWorld {\n \n public void method1() {\n int i = 5; //其作用范围是从声明的第4行,到其所处于的块结束12行位置\n System.out.println(i);\n { //子块\n System.out.println(i); //可以访问i\n int j = 6;\n System.out.println(j); //可以访问j\n }\n System.out.println(j); //不能访问j,因为其作用域到第10行就结束了\n }\n \n }\n ```\n\n\n\n- 静态变量\n\n ```java\n static int age;\t//(类变量)\n ```\n\n \n\n## final变量\n\n用final修饰的变量,只能赋值1次,因此常**用于定义常量**\n\n```java\npublic class HelloWorld {\n \n public void method1() {\n final int i = 5;\n \n i = 10; //i在第4行已经被赋值过了,所以这里会出现编译错误\n \n }\n \n}\n```\n\n但是对于引用类型的数据,变量存储的是地址,地址不可变,但地址里存放的值可以改变\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n final int []a = {1,2,3};\n a[1] = 10;\n a[2] = 100;\n \n System.out.println(a[1]);\n System.out.println(a[2]);\n \n // 注意这样写:a = {4,5,6}; 是错误的\n\n }\n}\n```\n\n输出如下![image-20210122203958650](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210122203958650.png)\n\n\n\n\n\n\n\n","slug":"javaSE/4-变量","published":1,"updated":"2021-08-29T15:30:50.603Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r20074akve5rp09eoj","content":"

变量

命名规则

    \n
  • 变量名只能使用 字母数字$_
  • \n
  • 变量第一个字符 只能使用 字母 、$、 _
  • \n
  • 使用完整单词命名
  • \n
\n

类型转换

    \n
  • 低精度向高精度转换可以正常转换

    \n
  • \n
  • 高精度向低精度转换有风险

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class HelloWorld {

    public static void main(String[] args) {

    byte b = 5;
    int i1 = 10;
    int i2 = 300;

    b = (byte) i1;
    //因为i1的值是在byte范围之内,所以即便进行强制转换
    //最后得到的值,也是10
    System.out.println(b);

    //因为i2的值是在byte范围之外,所以就会按照byte的长度进行截取
    //i2的值是300,其对应的二进制数是 100101100
    //按照byte的长度8位进行截取后,其值为 00101100 即44
    b =(byte) i2;
    System.out.println(b);

    //查看一个整数对应的二进制的方法:
    System.out.println(Integer.toBinaryString(i2));

    }
    }
    \n

    注意:short + short = int

    \n
  • \n
\n

变量分类

    \n
  • 成员变量(字段、属性、Field):

    \n

    当一个变量被声明在类下面,变量就叫做成员变量,或字段属性Field

    \n

    比如变量下面例子中,i就是一个属性。那么从第2行这个变量声明的位置开始,整个类都可以访问得到;所以其作用域就是从其声明的位置开始的整个类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloWorld {
    int i = 1;
    int j = i; //其他的属性可以访问i
    public void method1(){
    System.out.println(i); //方法1里可以访问i
    }
    public void method2(){
    System.out.println(i); //方法2里可以访问i
    }
    }
    \n
  • \n
  • 参数

    \n

    如果一个变量,是声明在一个方法上的,就叫做参数,参数的作用域即为该方法内的所有代码,在该方法外面不能使用该参数

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class HelloWorld {

    public void method1(int i){ //参数i的作用域即方法method1
    System.out.println(i);
    }
    public void method2(){

    System.out.println(i); //method2 不能访问参数i
    }

    int j = i; //类里面也不能访问参数i
    }
    \n
  • \n
  • 局部变量

    \n

    声明在方法内的变量,叫做局部变量,其作用域在声明开始的位置,到其所处于的块结束位置。

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class HelloWorld {

    public void method1() {
    int i = 5; //其作用范围是从声明的第4行,到其所处于的块结束12行位置
    System.out.println(i);
    { //子块
    System.out.println(i); //可以访问i
    int j = 6;
    System.out.println(j); //可以访问j
    }
    System.out.println(j); //不能访问j,因为其作用域到第10行就结束了
    }

    }
    \n
  • \n
\n
    \n
  • 静态变量

    \n
    1
    static int age;\t//(类变量)
    \n
  • \n
\n

final变量

用final修饰的变量,只能赋值1次,因此常用于定义常量

\n
1
2
3
4
5
6
7
8
9
10
public class HelloWorld {

public void method1() {
final int i = 5;

i = 10; //i在第4行已经被赋值过了,所以这里会出现编译错误

}

}
\n

但是对于引用类型的数据,变量存储的是地址,地址不可变,但地址里存放的值可以改变

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloWorld {
public static void main(String[] args) {
final int []a = {1,2,3};
a[1] = 10;
a[2] = 100;

System.out.println(a[1]);
System.out.println(a[2]);

// 注意这样写:a = {4,5,6}; 是错误的

}
}
\n

输出如下\"image-20210122203958650\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

变量

命名规则

    \n
  • 变量名只能使用 字母数字$_
  • \n
  • 变量第一个字符 只能使用 字母 、$、 _
  • \n
  • 使用完整单词命名
  • \n
\n

类型转换

    \n
  • 低精度向高精度转换可以正常转换

    \n
  • \n
  • 高精度向低精度转换有风险

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class HelloWorld {

    public static void main(String[] args) {

    byte b = 5;
    int i1 = 10;
    int i2 = 300;

    b = (byte) i1;
    //因为i1的值是在byte范围之内,所以即便进行强制转换
    //最后得到的值,也是10
    System.out.println(b);

    //因为i2的值是在byte范围之外,所以就会按照byte的长度进行截取
    //i2的值是300,其对应的二进制数是 100101100
    //按照byte的长度8位进行截取后,其值为 00101100 即44
    b =(byte) i2;
    System.out.println(b);

    //查看一个整数对应的二进制的方法:
    System.out.println(Integer.toBinaryString(i2));

    }
    }
    \n

    注意:short + short = int

    \n
  • \n
\n

变量分类

    \n
  • 成员变量(字段、属性、Field):

    \n

    当一个变量被声明在类下面,变量就叫做成员变量,或字段属性Field

    \n

    比如变量下面例子中,i就是一个属性。那么从第2行这个变量声明的位置开始,整个类都可以访问得到;所以其作用域就是从其声明的位置开始的整个类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class HelloWorld {
    int i = 1;
    int j = i; //其他的属性可以访问i
    public void method1(){
    System.out.println(i); //方法1里可以访问i
    }
    public void method2(){
    System.out.println(i); //方法2里可以访问i
    }
    }
    \n
  • \n
  • 参数

    \n

    如果一个变量,是声明在一个方法上的,就叫做参数,参数的作用域即为该方法内的所有代码,在该方法外面不能使用该参数

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class HelloWorld {

    public void method1(int i){ //参数i的作用域即方法method1
    System.out.println(i);
    }
    public void method2(){

    System.out.println(i); //method2 不能访问参数i
    }

    int j = i; //类里面也不能访问参数i
    }
    \n
  • \n
  • 局部变量

    \n

    声明在方法内的变量,叫做局部变量,其作用域在声明开始的位置,到其所处于的块结束位置。

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class HelloWorld {

    public void method1() {
    int i = 5; //其作用范围是从声明的第4行,到其所处于的块结束12行位置
    System.out.println(i);
    { //子块
    System.out.println(i); //可以访问i
    int j = 6;
    System.out.println(j); //可以访问j
    }
    System.out.println(j); //不能访问j,因为其作用域到第10行就结束了
    }

    }
    \n
  • \n
\n
    \n
  • 静态变量

    \n
    1
    static int age;\t//(类变量)
    \n
  • \n
\n

final变量

用final修饰的变量,只能赋值1次,因此常用于定义常量

\n
1
2
3
4
5
6
7
8
9
10
public class HelloWorld {

public void method1() {
final int i = 5;

i = 10; //i在第4行已经被赋值过了,所以这里会出现编译错误

}

}
\n

但是对于引用类型的数据,变量存储的是地址,地址不可变,但地址里存放的值可以改变

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloWorld {
public static void main(String[] args) {
final int []a = {1,2,3};
a[1] = 10;
a[2] = 100;

System.out.println(a[1]);
System.out.println(a[2]);

// 注意这样写:a = {4,5,6}; 是错误的

}
}
\n

输出如下\"image-20210122203958650\"

\n"},{"title":"6-输入语句","abbrlink":"63036c3c","date":"2021-02-12T05:37:38.000Z","description":"java输入语句Scanner的使用","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 输入语句\n\njava使用 `Scanner`语句进行输入操作,使用前需在前面加上:\n\n```java\nimport java.util.Scanner;\n```\n\n\n\n## 输入一个整数\n\n包括两个步骤:\n\n- 进入输入状态\n- 读取输入的指定类型的数据\n\n```java\nScanner s = new Scanner(System.in); //进入读取状态\nint a = s.nextInt(); //读取数据\n```\n\n\n\n## 输入一个浮点数\n\n```java\nScanner s = new Scanner(System.in);\nfloat b = s.nextFloat()\n```\n\n\n\n## 输入一个字符串\n\n```java\nScanner s = new Scanner(System.in);\nString s1 = s.nextLine();\nString s2 = s.next(); \n```\n\n\n\n## next()与nextLine()区别\n\nnext从第一个非空格字符开始读取,遇到空格就会停止(读取的数据不含空格),返回的是一个字符串\n\nnextLine读取一整行数据,遇到回车就停止(读取的数据不包含回车),返回的是一个字符串\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n public static void main(String[] args) {\n Scanner in = new Scanner(System.in);\n \n String s1 = in.next();\n String s2 = in.nextLine();\n \n System.out.print(s1);\n System.out.print(s2.length());\n System.out.print(\"===================\");\n }\n}\n```\n\n按下面格式输入:\n\n```\nabc\ndef\n```\n\n输出如下:\n\n```\nabc0===================\n```\n\n说明:`abc`是s1的内容,`0`是s2的长度(即s2什么也没得到)\n\n> next(),nextInt(),nextFloat()......这些只读取内容(不包含空格)部分,遇到空格或回车就截止了,把空格或回车放到了缓冲区\n>\n> nextLine()则是读取一整行,返回字符串,遇到回车(不读取回车)就截止\n>\n> 因此,如果在next()后使用nextLine(),在输入数据后,按下回车,则数据会被next()获取,\n>\n> **此时nextLine()紧接着进行读取,正好遇到回车,因此,nextLine什么也没得到。**\n","source":"_posts/javaSE/6-输入语句.md","raw":"---\ntitle: 6-输入语句\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 63036c3c\ndate: 2021-02-12 13:37:38\ndescription: java输入语句Scanner的使用\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 输入语句\n\njava使用 `Scanner`语句进行输入操作,使用前需在前面加上:\n\n```java\nimport java.util.Scanner;\n```\n\n\n\n## 输入一个整数\n\n包括两个步骤:\n\n- 进入输入状态\n- 读取输入的指定类型的数据\n\n```java\nScanner s = new Scanner(System.in); //进入读取状态\nint a = s.nextInt(); //读取数据\n```\n\n\n\n## 输入一个浮点数\n\n```java\nScanner s = new Scanner(System.in);\nfloat b = s.nextFloat()\n```\n\n\n\n## 输入一个字符串\n\n```java\nScanner s = new Scanner(System.in);\nString s1 = s.nextLine();\nString s2 = s.next(); \n```\n\n\n\n## next()与nextLine()区别\n\nnext从第一个非空格字符开始读取,遇到空格就会停止(读取的数据不含空格),返回的是一个字符串\n\nnextLine读取一整行数据,遇到回车就停止(读取的数据不包含回车),返回的是一个字符串\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n public static void main(String[] args) {\n Scanner in = new Scanner(System.in);\n \n String s1 = in.next();\n String s2 = in.nextLine();\n \n System.out.print(s1);\n System.out.print(s2.length());\n System.out.print(\"===================\");\n }\n}\n```\n\n按下面格式输入:\n\n```\nabc\ndef\n```\n\n输出如下:\n\n```\nabc0===================\n```\n\n说明:`abc`是s1的内容,`0`是s2的长度(即s2什么也没得到)\n\n> next(),nextInt(),nextFloat()......这些只读取内容(不包含空格)部分,遇到空格或回车就截止了,把空格或回车放到了缓冲区\n>\n> nextLine()则是读取一整行,返回字符串,遇到回车(不读取回车)就截止\n>\n> 因此,如果在next()后使用nextLine(),在输入数据后,按下回车,则数据会被next()获取,\n>\n> **此时nextLine()紧接着进行读取,正好遇到回车,因此,nextLine什么也没得到。**\n","slug":"javaSE/6-输入语句","published":1,"updated":"2021-08-29T15:30:57.135Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r30078akvehflj72w7","content":"

输入语句

java使用 Scanner语句进行输入操作,使用前需在前面加上:

\n
1
import java.util.Scanner;
\n

输入一个整数

包括两个步骤:

\n
    \n
  • 进入输入状态
  • \n
  • 读取输入的指定类型的数据
  • \n
\n
1
2
Scanner s = new Scanner(System.in);      //进入读取状态
int a = s.nextInt(); //读取数据
\n

输入一个浮点数

1
2
Scanner s = new Scanner(System.in);
float b = s.nextFloat()
\n

输入一个字符串

1
2
3
Scanner s = new Scanner(System.in);
String s1 = s.nextLine();
String s2 = s.next();
\n

next()与nextLine()区别

next从第一个非空格字符开始读取,遇到空格就会停止(读取的数据不含空格),返回的是一个字符串

\n

nextLine读取一整行数据,遇到回车就停止(读取的数据不包含回车),返回的是一个字符串

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);

String s1 = in.next();
String s2 = in.nextLine();

System.out.print(s1);
System.out.print(s2.length());
System.out.print("===================");
}
}
\n

按下面格式输入:

\n
1
2
abc
def
\n

输出如下:

\n
1
abc0===================
\n

说明:abc是s1的内容,0是s2的长度(即s2什么也没得到)

\n
\n

next(),nextInt(),nextFloat()……这些只读取内容(不包含空格)部分,遇到空格或回车就截止了,把空格或回车放到了缓冲区

\n

nextLine()则是读取一整行,返回字符串,遇到回车(不读取回车)就截止

\n

因此,如果在next()后使用nextLine(),在输入数据后,按下回车,则数据会被next()获取,

\n

此时nextLine()紧接着进行读取,正好遇到回车,因此,nextLine什么也没得到。

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

输入语句

java使用 Scanner语句进行输入操作,使用前需在前面加上:

\n
1
import java.util.Scanner;
\n

输入一个整数

包括两个步骤:

\n
    \n
  • 进入输入状态
  • \n
  • 读取输入的指定类型的数据
  • \n
\n
1
2
Scanner s = new Scanner(System.in);      //进入读取状态
int a = s.nextInt(); //读取数据
\n

输入一个浮点数

1
2
Scanner s = new Scanner(System.in);
float b = s.nextFloat()
\n

输入一个字符串

1
2
3
Scanner s = new Scanner(System.in);
String s1 = s.nextLine();
String s2 = s.next();
\n

next()与nextLine()区别

next从第一个非空格字符开始读取,遇到空格就会停止(读取的数据不含空格),返回的是一个字符串

\n

nextLine读取一整行数据,遇到回车就停止(读取的数据不包含回车),返回的是一个字符串

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Scanner;

public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);

String s1 = in.next();
String s2 = in.nextLine();

System.out.print(s1);
System.out.print(s2.length());
System.out.print("===================");
}
}
\n

按下面格式输入:

\n
1
2
abc
def
\n

输出如下:

\n
1
abc0===================
\n

说明:abc是s1的内容,0是s2的长度(即s2什么也没得到)

\n
\n

next(),nextInt(),nextFloat()……这些只读取内容(不包含空格)部分,遇到空格或回车就截止了,把空格或回车放到了缓冲区

\n

nextLine()则是读取一整行,返回字符串,遇到回车(不读取回车)就截止

\n

因此,如果在next()后使用nextLine(),在输入数据后,按下回车,则数据会被next()获取,

\n

此时nextLine()紧接着进行读取,正好遇到回车,因此,nextLine什么也没得到。

\n
\n"},{"title":"7-条件语句","abbrlink":"b54f36d8","date":"2021-02-12T07:20:47.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","description":"java条件语句if...else..., switch","_content":"\n# 条件语句\n\n## if语句\n\n- 3种基本用法\n\n ```java\n ----------1------------\n if(){\n \n }\n ----------2------------\n if(){\n \n }\n else{\n \n }\n ----------3------------\n \n if(){\n \n }\n else if(){\n \n }\n else if(){\n \n }\n \n else{\n \n }\n -----------------------\n ```\n\n## switch语句\n\n- switch可以使用`byte,short,int,char,String,enum`\n- 每个表达式结束,都应该有一个break\n- String在Java1.7之前是不支持的, Java从1.7开始支持switch用String的,编译后是把String转化为hash值,其实还是整数\n- enum是枚举类型\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n \n int day = 5;\n\n switch(day){\n case 1:\n System.out.println(\"星期一\");\n break;\n case 2:\n System.out.println(\"星期二\");\n break;\n case 3:\n System.out.println(\"星期三\");\n break;\n case 4:\n System.out.println(\"星期四\");\n break;\n case 5:\n System.out.println(\"星期五\");\n break;\n case 6:\n System.out.println(\"星期六\");\n break;\n case 7:\n System.out.println(\"星期天\");\n break;\n default:\n System.out.println(\"这个是什么鬼?\");\n }\n \n }\n}\n```\n\n","source":"_posts/javaSE/7-条件语句.md","raw":"---\ntitle: 7-条件语句\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: b54f36d8\ndate: 2021-02-12 15:20:47\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\ndescription: java条件语句if...else..., switch\n---\n\n# 条件语句\n\n## if语句\n\n- 3种基本用法\n\n ```java\n ----------1------------\n if(){\n \n }\n ----------2------------\n if(){\n \n }\n else{\n \n }\n ----------3------------\n \n if(){\n \n }\n else if(){\n \n }\n else if(){\n \n }\n \n else{\n \n }\n -----------------------\n ```\n\n## switch语句\n\n- switch可以使用`byte,short,int,char,String,enum`\n- 每个表达式结束,都应该有一个break\n- String在Java1.7之前是不支持的, Java从1.7开始支持switch用String的,编译后是把String转化为hash值,其实还是整数\n- enum是枚举类型\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n \n int day = 5;\n\n switch(day){\n case 1:\n System.out.println(\"星期一\");\n break;\n case 2:\n System.out.println(\"星期二\");\n break;\n case 3:\n System.out.println(\"星期三\");\n break;\n case 4:\n System.out.println(\"星期四\");\n break;\n case 5:\n System.out.println(\"星期五\");\n break;\n case 6:\n System.out.println(\"星期六\");\n break;\n case 7:\n System.out.println(\"星期天\");\n break;\n default:\n System.out.println(\"这个是什么鬼?\");\n }\n \n }\n}\n```\n\n","slug":"javaSE/7-条件语句","published":1,"updated":"2021-08-30T08:29:26.817Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r5007cakve2yiob0j3","content":"

条件语句

if语句

    \n
  • 3种基本用法

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    ----------1------------
    if(){

    }
    ----------2------------
    if(){

    }
    else{

    }
    ----------3------------

    if(){

    }
    else if(){

    }
    else if(){

    }

    else{

    }
    -----------------------
    \n
  • \n
\n

switch语句

    \n
  • switch可以使用byte,short,int,char,String,enum
  • \n
  • 每个表达式结束,都应该有一个break
  • \n
  • String在Java1.7之前是不支持的, Java从1.7开始支持switch用String的,编译后是把String转化为hash值,其实还是整数
  • \n
  • enum是枚举类型
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class HelloWorld {
public static void main(String[] args) {

int day = 5;

switch(day){
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
case 4:
System.out.println("星期四");
break;
case 5:
System.out.println("星期五");
break;
case 6:
System.out.println("星期六");
break;
case 7:
System.out.println("星期天");
break;
default:
System.out.println("这个是什么鬼?");
}

}
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

条件语句

if语句

    \n
  • 3种基本用法

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    ----------1------------
    if(){

    }
    ----------2------------
    if(){

    }
    else{

    }
    ----------3------------

    if(){

    }
    else if(){

    }
    else if(){

    }

    else{

    }
    -----------------------
    \n
  • \n
\n

switch语句

    \n
  • switch可以使用byte,short,int,char,String,enum
  • \n
  • 每个表达式结束,都应该有一个break
  • \n
  • String在Java1.7之前是不支持的, Java从1.7开始支持switch用String的,编译后是把String转化为hash值,其实还是整数
  • \n
  • enum是枚举类型
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class HelloWorld {
public static void main(String[] args) {

int day = 5;

switch(day){
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
case 4:
System.out.println("星期四");
break;
case 5:
System.out.println("星期五");
break;
case 6:
System.out.println("星期六");
break;
case 7:
System.out.println("星期天");
break;
default:
System.out.println("这个是什么鬼?");
}

}
}
\n"},{"title":"9-package包","abbrlink":"94566522","date":"2021-02-13T09:18:41.000Z","description":"java中package使用介绍,可结合Java访问修饰符学习","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 包package\n\n- 通常会把比较接近的类,规划在同一个包下;\n\n- 在最开始的地方声明该类所处于的包名;\n\n ```java\n package charactor; //在最开始的地方声明该类所处于的包名\n public class Hero {\n \n String name; //姓名\n float hp; //血量\n int moveSpeed; //移动速度\n \n }\n ```\n\n \n\n- 使用同一个包下的其他类,直接使用即可\n\n- 使用其他包下的类,必须`import`\n\n![把比较接近的类,规划在同一个包下](https://stepimagewm.how2j.cn/600.png)\n\n\n\n```java\npackage charactor;\n \n//Weapon类在其他包里,使用必须进行import\nimport property.Weapon;\n \npublic class Hero {\n \n \n}\n```\n\n","source":"_posts/javaSE/9-package包.md","raw":"---\ntitle: 9-package包\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: '94566522'\ndate: 2021-02-13 17:18:41\ndescription: java中package使用介绍,可结合Java访问修饰符学习\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 包package\n\n- 通常会把比较接近的类,规划在同一个包下;\n\n- 在最开始的地方声明该类所处于的包名;\n\n ```java\n package charactor; //在最开始的地方声明该类所处于的包名\n public class Hero {\n \n String name; //姓名\n float hp; //血量\n int moveSpeed; //移动速度\n \n }\n ```\n\n \n\n- 使用同一个包下的其他类,直接使用即可\n\n- 使用其他包下的类,必须`import`\n\n![把比较接近的类,规划在同一个包下](https://stepimagewm.how2j.cn/600.png)\n\n\n\n```java\npackage charactor;\n \n//Weapon类在其他包里,使用必须进行import\nimport property.Weapon;\n \npublic class Hero {\n \n \n}\n```\n\n","slug":"javaSE/9-package包","published":1,"updated":"2021-08-29T15:31:14.685Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r5007fakve7bba4t62","content":"

包package

    \n
  • 通常会把比较接近的类,规划在同一个包下;

    \n
  • \n
  • 在最开始的地方声明该类所处于的包名;

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    package charactor; //在最开始的地方声明该类所处于的包名
    public class Hero {

    String name; //姓名
    float hp; //血量
    int moveSpeed; //移动速度

    }
    \n
  • \n
\n
    \n
  • 使用同一个包下的其他类,直接使用即可

    \n
  • \n
  • 使用其他包下的类,必须import

    \n
  • \n
\n

\"把比较接近的类,规划在同一个包下\"

\n
1
2
3
4
5
6
7
8
9
package charactor;

//Weapon类在其他包里,使用必须进行import
import property.Weapon;

public class Hero {


}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

包package

    \n
  • 通常会把比较接近的类,规划在同一个包下;

    \n
  • \n
  • 在最开始的地方声明该类所处于的包名;

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    package charactor; //在最开始的地方声明该类所处于的包名
    public class Hero {

    String name; //姓名
    float hp; //血量
    int moveSpeed; //移动速度

    }
    \n
  • \n
\n
    \n
  • 使用同一个包下的其他类,直接使用即可

    \n
  • \n
  • 使用其他包下的类,必须import

    \n
  • \n
\n

\"把比较接近的类,规划在同一个包下\"

\n
1
2
3
4
5
6
7
8
9
package charactor;

//Weapon类在其他包里,使用必须进行import
import property.Weapon;

public class Hero {


}
\n"},{"title":"win10任务栏透明 - win10","description":"Win10任务栏设置透明","cover":"https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png","abbrlink":"3de0f4a2","date":"2021-09-06T04:21:11.000Z","swiper_index":null,"_content":"\n\n\n### Win10使任务栏完全透明的方法\n\n1、桌面鼠标右键点击个性化,颜色》》选择颜色》》选择深色或自定义,自定义可按如下设置。\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H216041O53.jpg)\n\n\n\n2、win+R 输入regedit(也就是[注册表])注意以管理员身份运行!\n\n\n\n3、定位到以下目录\n\n`计算机\\HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced`\n\n右键该目录,选择新建→DWORD (32位) 值。\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160441G1.jpg)\n\n4、重命名此文件为TaskbarAcrylicOpacity。\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160449332.jpg)\n\n\n\n5、双击文件名称,然后按照红圈设置。0为完全透明,10为完全不透明,中间的值可以自己调整以达到适合的透明度.\n\n注意基数选择【十进制】!然后点击确定。\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H216045V50.jpg)\n\n\n\n6、重启Windows资源管理器,或者重启电脑,效果:\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160505S1.jpg)","source":"_posts/win10/win10任务栏透明.md","raw":"---\ntitle: win10任务栏透明 - win10\ncategories: win10\ndescription: Win10任务栏设置透明\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png'\nabbrlink: 3de0f4a2\ndate: 2021-09-06 12:21:11\nswiper_index:\n---\n\n\n\n### Win10使任务栏完全透明的方法\n\n1、桌面鼠标右键点击个性化,颜色》》选择颜色》》选择深色或自定义,自定义可按如下设置。\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H216041O53.jpg)\n\n\n\n2、win+R 输入regedit(也就是[注册表])注意以管理员身份运行!\n\n\n\n3、定位到以下目录\n\n`计算机\\HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced`\n\n右键该目录,选择新建→DWORD (32位) 值。\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160441G1.jpg)\n\n4、重命名此文件为TaskbarAcrylicOpacity。\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160449332.jpg)\n\n\n\n5、双击文件名称,然后按照红圈设置。0为完全透明,10为完全不透明,中间的值可以自己调整以达到适合的透明度.\n\n注意基数选择【十进制】!然后点击确定。\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H216045V50.jpg)\n\n\n\n6、重启Windows资源管理器,或者重启电脑,效果:\n\n\n\n![Win10如何使任务栏完全透明?](http://www.xitongzhijia.net/uploads/allimg/210722/138-210H2160505S1.jpg)","slug":"win10/win10任务栏透明","published":1,"updated":"2021-09-07T14:29:17.913Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r6007jakvegi6bdbyo","content":"

Win10使任务栏完全透明的方法

1、桌面鼠标右键点击个性化,颜色》》选择颜色》》选择深色或自定义,自定义可按如下设置。

\n

\"Win10如何使任务栏完全透明?\"

\n

2、win+R 输入regedit(也就是[注册表])注意以管理员身份运行!

\n

3、定位到以下目录

\n

计算机\\HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced

\n

右键该目录,选择新建→DWORD (32位) 值。

\n

\"Win10如何使任务栏完全透明?\"

\n

4、重命名此文件为TaskbarAcrylicOpacity。

\n

\"Win10如何使任务栏完全透明?\"

\n

5、双击文件名称,然后按照红圈设置。0为完全透明,10为完全不透明,中间的值可以自己调整以达到适合的透明度.

\n

注意基数选择【十进制】!然后点击确定。

\n

\"Win10如何使任务栏完全透明?\"

\n

6、重启Windows资源管理器,或者重启电脑,效果:

\n

\"Win10如何使任务栏完全透明?\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

Win10使任务栏完全透明的方法

1、桌面鼠标右键点击个性化,颜色》》选择颜色》》选择深色或自定义,自定义可按如下设置。

\n

\"Win10如何使任务栏完全透明?\"

\n

2、win+R 输入regedit(也就是[注册表])注意以管理员身份运行!

\n

3、定位到以下目录

\n

计算机\\HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced

\n

右键该目录,选择新建→DWORD (32位) 值。

\n

\"Win10如何使任务栏完全透明?\"

\n

4、重命名此文件为TaskbarAcrylicOpacity。

\n

\"Win10如何使任务栏完全透明?\"

\n

5、双击文件名称,然后按照红圈设置。0为完全透明,10为完全不透明,中间的值可以自己调整以达到适合的透明度.

\n

注意基数选择【十进制】!然后点击确定。

\n

\"Win10如何使任务栏完全透明?\"

\n

6、重启Windows资源管理器,或者重启电脑,效果:

\n

\"Win10如何使任务栏完全透明?\"

\n"},{"title":"8-循环语句","abbrlink":"753dfa3e","date":"2021-02-12T06:46:38.000Z","description":"while、do while、for、增强型for、continue、break","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 循环语句\n\n## while与 do while\n\n| 语句 | 含义 |\n| :------: | :-----------------------------------: |\n| while | 条件为true时 重复执行 |\n| do while | 条件为true时 重复执行,至少会执行一次 |\n\n用法:\n\n```java\n//while:\nwhile(){\n \n}\n-------------------------\n//do while\ndo{\n \n}while();\n```\n\n## for语句与增强型for语句\n\n### for语句\n\n用法(类似c/c++语言):\n\n```java\nfor(语句1;语句2;语句3){\n\t语句4;\n}\n```\n\n### 增强型for语句 \n\nJava5引入了一种主要用于**数组**的增强型for循环,用法如下:\n\n```java\nfor(局部变量:表达式){\n\n}\n\n/* 局部变量:该变量的类型必须和数组元素的类型匹配。\n * 其作用域限定在循环语句块,其值与此时数组元素的值相等。*/\n\n// 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。\n```\n\n- **声明语句:**声明新的局部变量,该**变量的类型必须和数组元素的类型匹配**。其作用域限定在循环语句块,其值与此时数组元素的值相等。\n\n- **表达式:**表达式是要访问的数组名,或者是**返回值为数组**的**方法**。\n\n## contiue语句\n\n满足条件时跳过本次循环,进入下一次循环\n\n## break语句\n\n满足条件时终止**距离最近**的一层循环;\n\n## 使用标签结束外部循环\n\n注意标签放的位置:需要终止的循环外部\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n \n //打印单数 \n outloop: //outloop这个标签可以自定义,比如a,b,c,outloop1...,放在需要终止的循环外部\n for (int i = 0; i < 10; i++) {\n \n for (int j = 0; j < 10; j++) {\n System.out.println(i+\":\"+j);\n if(0==j%2) \n break outloop; //如果是双数,结束外部循环\n }\n \n }\n \n }\n}\n```\n\n","source":"_posts/javaSE/8-循环语句.md","raw":"---\ntitle: 8-循环语句\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 753dfa3e\ndate: 2021-02-12 14:46:38\ndescription: while、do while、for、增强型for、continue、break\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 循环语句\n\n## while与 do while\n\n| 语句 | 含义 |\n| :------: | :-----------------------------------: |\n| while | 条件为true时 重复执行 |\n| do while | 条件为true时 重复执行,至少会执行一次 |\n\n用法:\n\n```java\n//while:\nwhile(){\n \n}\n-------------------------\n//do while\ndo{\n \n}while();\n```\n\n## for语句与增强型for语句\n\n### for语句\n\n用法(类似c/c++语言):\n\n```java\nfor(语句1;语句2;语句3){\n\t语句4;\n}\n```\n\n### 增强型for语句 \n\nJava5引入了一种主要用于**数组**的增强型for循环,用法如下:\n\n```java\nfor(局部变量:表达式){\n\n}\n\n/* 局部变量:该变量的类型必须和数组元素的类型匹配。\n * 其作用域限定在循环语句块,其值与此时数组元素的值相等。*/\n\n// 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。\n```\n\n- **声明语句:**声明新的局部变量,该**变量的类型必须和数组元素的类型匹配**。其作用域限定在循环语句块,其值与此时数组元素的值相等。\n\n- **表达式:**表达式是要访问的数组名,或者是**返回值为数组**的**方法**。\n\n## contiue语句\n\n满足条件时跳过本次循环,进入下一次循环\n\n## break语句\n\n满足条件时终止**距离最近**的一层循环;\n\n## 使用标签结束外部循环\n\n注意标签放的位置:需要终止的循环外部\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n \n //打印单数 \n outloop: //outloop这个标签可以自定义,比如a,b,c,outloop1...,放在需要终止的循环外部\n for (int i = 0; i < 10; i++) {\n \n for (int j = 0; j < 10; j++) {\n System.out.println(i+\":\"+j);\n if(0==j%2) \n break outloop; //如果是双数,结束外部循环\n }\n \n }\n \n }\n}\n```\n\n","slug":"javaSE/8-循环语句","published":1,"updated":"2021-08-29T15:31:11.601Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r7007lakve2vjjaimr","content":"

循环语句

while与 do while

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
语句含义
while条件为true时 重复执行
do while条件为true时 重复执行,至少会执行一次
\n
\n

用法:

\n
1
2
3
4
5
6
7
8
9
//while:
while(){

}
-------------------------
//do while
do{

}while();
\n

for语句与增强型for语句

for语句

用法(类似c/c++语言):

\n
1
2
3
for(语句1;语句2;语句3){
\t语句4;
}
\n

增强型for语句

Java5引入了一种主要用于数组的增强型for循环,用法如下:

\n
1
2
3
4
5
6
7
8
for(局部变量:表达式){

}

/* 局部变量:该变量的类型必须和数组元素的类型匹配。
* 其作用域限定在循环语句块,其值与此时数组元素的值相等。*/

// 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
\n
    \n
  • 声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。

    \n
  • \n
  • 表达式:表达式是要访问的数组名,或者是返回值为数组方法

    \n
  • \n
\n

contiue语句

满足条件时跳过本次循环,进入下一次循环

\n

break语句

满足条件时终止距离最近的一层循环;

\n

使用标签结束外部循环

注意标签放的位置:需要终止的循环外部

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloWorld {
public static void main(String[] args) {

//打印单数
outloop: //outloop这个标签可以自定义,比如a,b,c,outloop1...,放在需要终止的循环外部
for (int i = 0; i < 10; i++) {

for (int j = 0; j < 10; j++) {
System.out.println(i+":"+j);
if(0==j%2)
break outloop; //如果是双数,结束外部循环
}

}

}
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

循环语句

while与 do while

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
语句含义
while条件为true时 重复执行
do while条件为true时 重复执行,至少会执行一次
\n
\n

用法:

\n
1
2
3
4
5
6
7
8
9
//while:
while(){

}
-------------------------
//do while
do{

}while();
\n

for语句与增强型for语句

for语句

用法(类似c/c++语言):

\n
1
2
3
for(语句1;语句2;语句3){
\t语句4;
}
\n

增强型for语句

Java5引入了一种主要用于数组的增强型for循环,用法如下:

\n
1
2
3
4
5
6
7
8
for(局部变量:表达式){

}

/* 局部变量:该变量的类型必须和数组元素的类型匹配。
* 其作用域限定在循环语句块,其值与此时数组元素的值相等。*/

// 表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
\n
    \n
  • 声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。

    \n
  • \n
  • 表达式:表达式是要访问的数组名,或者是返回值为数组方法

    \n
  • \n
\n

contiue语句

满足条件时跳过本次循环,进入下一次循环

\n

break语句

满足条件时终止距离最近的一层循环;

\n

使用标签结束外部循环

注意标签放的位置:需要终止的循环外部

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloWorld {
public static void main(String[] args) {

//打印单数
outloop: //outloop这个标签可以自定义,比如a,b,c,outloop1...,放在需要终止的循环外部
for (int i = 0; i < 10; i++) {

for (int j = 0; j < 10; j++) {
System.out.println(i+":"+j);
if(0==j%2)
break outloop; //如果是双数,结束外部循环
}

}

}
}
\n"},{"title":"win10主题美化","description":"Win10系统美化记录","cover":"https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png","abbrlink":"54a4218","swiper_index":4,"date":"2021-09-05T06:20:22.000Z","_content":"\n\n\n\n\n\n\n### 主题安装\n\n#### 主题破解补丁安装\n\n下载 [主题破解补丁](https://zhutix.com/tools/win-theme-patcher/) 到桌面,在“UltraUXThemePatcher.exe”上右键 – 以管理员身份运行(必须),点击Next,并一路同意直到安装成功,重启电脑。\n\n\n\n#### 安装主题\n\n打开你下载的主题压缩包,如果无法打开请先安装一款解压软件,比如 [WinRAR](https://zhutix.com/software/winrar-ha/) 或 [360压缩](https://yasuo.360.cn/),在里面找到主题安装文件,一般在“主题”文件夹内,或“***_TW10.exe”类型的文件,运行,不要改变安装路径(**绝对不要**),安装;完成后依次在桌面上 > 右键 > 个性化 > [主题](ms-settings:themes),能看到刚安装的主题样式,鼠标左键点击即可应用。\n\n\n\n#### 调整开始菜单及任务栏样式\n\n下载 [开始菜单程序](https://zhutix.com/tools/startisback-plus-pojie/) 到桌面,右键 > 以管理员身份运行“StartIsBack.exe”,根据需要选择“为我安装”或“为所有人安装”,不要改变安装路径,安装;完成后**在开始按钮处右键 > 属性,以打开配置面板**,在外观里面可以更换开始菜单及任务栏的样式、透明度等…\n\n\n\n\n\n### 图标\n\n#### 修改系统图标\n\n系统图标就是Win10系统自带的那些图标,比如文件夹、硬盘、控制面板等图标,不包括第三方软件的哦,像QQ、微信、酷狗之类的软件图标,不在此项,前后对比见下图:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_220614.jpg)\n\n目前致美化有两种图标包:iPack([素材](https://zhutix.com/tag/ipack/) · [教程](https://zhutix.com/study/ipack-ff/))、7tsp([素材](https://zhutix.com/tag/7tsp/) · [教程](https://zhutix.com/tools/7tsp-gui/)),可以一次性更改90%的系统图标,用途及效果虽然一样,但两种图标包的使用方法和兼容性不相同,使用前必须查阅相应教程;其中iPack图标包为.exe安装程序,兼容全部Win10系统,但风险较高;7tsp图标包为.7z文件,需要借助[7TSP GUI](https://zhutix.com/tools/7tsp-gui/)加载,仅兼容Win10 1903之后的系统,风险较低。\n\n很多主题,作者都是搭配好图标包的,有些直接包含在了主题压缩包内,有些是要下载的,以 [陶瓷灰](https://zhutix.com/pc/porcelain-vs/) 这款Win10主题的图标为例,我们打开主题压缩包:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_221330.jpg)\n\n你能看到里面有一个“图标.url”,双击打开它,即可在浏览器里面加载到图标下载页面,下载后,打开图标压缩包:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_221717.jpg)\n\n里面包含了“7tsp”和“iPack”文件夹,我们选择其中一种即可;再次说明,不论你选择的是哪一种,都必须先看下相应的教程再安装:[iPack图标包安装注意事项](https://zhutix.com/study/ipack-ff/)\n\n#### 修改桌面软件图标\n\n软件图标就是前面提到的像QQ、微信之类的啦:\n\n![img](https://dl.zhutix.net/2019/10/zhuomico-01.png)\n\n桌面上的图标分为两类:系统工具图标,如:此电脑、网络、文档、回收站;软件快捷方式图标,如:QQ、微信、360浏览器等…详细方法请参考:[桌面图标更改方法](https://zhutix.com/study/zhuomian-ruanjian/)\n\n\n\n\n\n#### 修改任务栏软件图标\n\n![img](https://dl.zhutix.net/2020/07/456234.jpg)\n\n任务栏软件图标,就是被你手动固定在任务栏上的那些软件的图标,如上图,详细方法请参考:[任务栏软件图标修改方法](https://zhutix.com/study/taskbar-icon-c/)\n\n![img](https://dl.zhutix.net/2017/03/rwlczcxtbxgff06.jpg?x-oss-process=image/resize,m_fill,h_120,w_168)\n\n![img](https://dl.zhutix.net/2017/03/rwlczcxtbxgff06.jpg?x-oss-process=image/resize,m_fill,h_120,w_168)\n\n\n\n\n\n","source":"_posts/win10/win10主题美化.md","raw":"---\ntitle: win10主题美化\ntags:\n - win10\ncategories: win10\ndescription: Win10系统美化记录\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png'\nabbrlink: 54a4218\nswiper_index: 4\ndate: 2021-09-05 14:20:22\n---\n\n\n\n\n\n\n\n### 主题安装\n\n#### 主题破解补丁安装\n\n下载 [主题破解补丁](https://zhutix.com/tools/win-theme-patcher/) 到桌面,在“UltraUXThemePatcher.exe”上右键 – 以管理员身份运行(必须),点击Next,并一路同意直到安装成功,重启电脑。\n\n\n\n#### 安装主题\n\n打开你下载的主题压缩包,如果无法打开请先安装一款解压软件,比如 [WinRAR](https://zhutix.com/software/winrar-ha/) 或 [360压缩](https://yasuo.360.cn/),在里面找到主题安装文件,一般在“主题”文件夹内,或“***_TW10.exe”类型的文件,运行,不要改变安装路径(**绝对不要**),安装;完成后依次在桌面上 > 右键 > 个性化 > [主题](ms-settings:themes),能看到刚安装的主题样式,鼠标左键点击即可应用。\n\n\n\n#### 调整开始菜单及任务栏样式\n\n下载 [开始菜单程序](https://zhutix.com/tools/startisback-plus-pojie/) 到桌面,右键 > 以管理员身份运行“StartIsBack.exe”,根据需要选择“为我安装”或“为所有人安装”,不要改变安装路径,安装;完成后**在开始按钮处右键 > 属性,以打开配置面板**,在外观里面可以更换开始菜单及任务栏的样式、透明度等…\n\n\n\n\n\n### 图标\n\n#### 修改系统图标\n\n系统图标就是Win10系统自带的那些图标,比如文件夹、硬盘、控制面板等图标,不包括第三方软件的哦,像QQ、微信、酷狗之类的软件图标,不在此项,前后对比见下图:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_220614.jpg)\n\n目前致美化有两种图标包:iPack([素材](https://zhutix.com/tag/ipack/) · [教程](https://zhutix.com/study/ipack-ff/))、7tsp([素材](https://zhutix.com/tag/7tsp/) · [教程](https://zhutix.com/tools/7tsp-gui/)),可以一次性更改90%的系统图标,用途及效果虽然一样,但两种图标包的使用方法和兼容性不相同,使用前必须查阅相应教程;其中iPack图标包为.exe安装程序,兼容全部Win10系统,但风险较高;7tsp图标包为.7z文件,需要借助[7TSP GUI](https://zhutix.com/tools/7tsp-gui/)加载,仅兼容Win10 1903之后的系统,风险较低。\n\n很多主题,作者都是搭配好图标包的,有些直接包含在了主题压缩包内,有些是要下载的,以 [陶瓷灰](https://zhutix.com/pc/porcelain-vs/) 这款Win10主题的图标为例,我们打开主题压缩包:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_221330.jpg)\n\n你能看到里面有一个“图标.url”,双击打开它,即可在浏览器里面加载到图标下载页面,下载后,打开图标压缩包:\n\n![img](https://dl.zhutix.net/2020/07/2021-01-12_221717.jpg)\n\n里面包含了“7tsp”和“iPack”文件夹,我们选择其中一种即可;再次说明,不论你选择的是哪一种,都必须先看下相应的教程再安装:[iPack图标包安装注意事项](https://zhutix.com/study/ipack-ff/)\n\n#### 修改桌面软件图标\n\n软件图标就是前面提到的像QQ、微信之类的啦:\n\n![img](https://dl.zhutix.net/2019/10/zhuomico-01.png)\n\n桌面上的图标分为两类:系统工具图标,如:此电脑、网络、文档、回收站;软件快捷方式图标,如:QQ、微信、360浏览器等…详细方法请参考:[桌面图标更改方法](https://zhutix.com/study/zhuomian-ruanjian/)\n\n\n\n\n\n#### 修改任务栏软件图标\n\n![img](https://dl.zhutix.net/2020/07/456234.jpg)\n\n任务栏软件图标,就是被你手动固定在任务栏上的那些软件的图标,如上图,详细方法请参考:[任务栏软件图标修改方法](https://zhutix.com/study/taskbar-icon-c/)\n\n![img](https://dl.zhutix.net/2017/03/rwlczcxtbxgff06.jpg?x-oss-process=image/resize,m_fill,h_120,w_168)\n\n![img](https://dl.zhutix.net/2017/03/rwlczcxtbxgff06.jpg?x-oss-process=image/resize,m_fill,h_120,w_168)\n\n\n\n\n\n","slug":"win10/win10主题美化","published":1,"updated":"2021-09-05T09:32:32.290Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r8007qakvebtub8ep8","content":"

主题安装

主题破解补丁安装

下载 主题破解补丁 到桌面,在“UltraUXThemePatcher.exe”上右键 – 以管理员身份运行(必须),点击Next,并一路同意直到安装成功,重启电脑。

\n

安装主题

打开你下载的主题压缩包,如果无法打开请先安装一款解压软件,比如 WinRAR360压缩,在里面找到主题安装文件,一般在“主题”文件夹内,或“*_TW10.exe”类型的文件,运行,不要改变安装路径(绝对不要**),安装;完成后依次在桌面上 > 右键 > 个性化 > 主题,能看到刚安装的主题样式,鼠标左键点击即可应用。

\n

调整开始菜单及任务栏样式

下载 开始菜单程序 到桌面,右键 > 以管理员身份运行“StartIsBack.exe”,根据需要选择“为我安装”或“为所有人安装”,不要改变安装路径,安装;完成后在开始按钮处右键 > 属性,以打开配置面板,在外观里面可以更换开始菜单及任务栏的样式、透明度等…

\n

图标

修改系统图标

系统图标就是Win10系统自带的那些图标,比如文件夹、硬盘、控制面板等图标,不包括第三方软件的哦,像QQ、微信、酷狗之类的软件图标,不在此项,前后对比见下图:

\n

\"img\"

\n

目前致美化有两种图标包:iPack(素材 · 教程)、7tsp(素材 · 教程),可以一次性更改90%的系统图标,用途及效果虽然一样,但两种图标包的使用方法和兼容性不相同,使用前必须查阅相应教程;其中iPack图标包为.exe安装程序,兼容全部Win10系统,但风险较高;7tsp图标包为.7z文件,需要借助7TSP GUI加载,仅兼容Win10 1903之后的系统,风险较低。

\n

很多主题,作者都是搭配好图标包的,有些直接包含在了主题压缩包内,有些是要下载的,以 陶瓷灰 这款Win10主题的图标为例,我们打开主题压缩包:

\n

\"img\"

\n

你能看到里面有一个“图标.url”,双击打开它,即可在浏览器里面加载到图标下载页面,下载后,打开图标压缩包:

\n

\"img\"

\n

里面包含了“7tsp”和“iPack”文件夹,我们选择其中一种即可;再次说明,不论你选择的是哪一种,都必须先看下相应的教程再安装:iPack图标包安装注意事项

\n

修改桌面软件图标

软件图标就是前面提到的像QQ、微信之类的啦:

\n

\"img\"

\n

桌面上的图标分为两类:系统工具图标,如:此电脑、网络、文档、回收站;软件快捷方式图标,如:QQ、微信、360浏览器等…详细方法请参考:桌面图标更改方法

\n

修改任务栏软件图标

\"img\"

\n

任务栏软件图标,就是被你手动固定在任务栏上的那些软件的图标,如上图,详细方法请参考:任务栏软件图标修改方法

\n

\"img\"

\n

\"img\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

主题安装

主题破解补丁安装

下载 主题破解补丁 到桌面,在“UltraUXThemePatcher.exe”上右键 – 以管理员身份运行(必须),点击Next,并一路同意直到安装成功,重启电脑。

\n

安装主题

打开你下载的主题压缩包,如果无法打开请先安装一款解压软件,比如 WinRAR360压缩,在里面找到主题安装文件,一般在“主题”文件夹内,或“*_TW10.exe”类型的文件,运行,不要改变安装路径(绝对不要**),安装;完成后依次在桌面上 > 右键 > 个性化 > 主题,能看到刚安装的主题样式,鼠标左键点击即可应用。

\n

调整开始菜单及任务栏样式

下载 开始菜单程序 到桌面,右键 > 以管理员身份运行“StartIsBack.exe”,根据需要选择“为我安装”或“为所有人安装”,不要改变安装路径,安装;完成后在开始按钮处右键 > 属性,以打开配置面板,在外观里面可以更换开始菜单及任务栏的样式、透明度等…

\n

图标

修改系统图标

系统图标就是Win10系统自带的那些图标,比如文件夹、硬盘、控制面板等图标,不包括第三方软件的哦,像QQ、微信、酷狗之类的软件图标,不在此项,前后对比见下图:

\n

\"img\"

\n

目前致美化有两种图标包:iPack(素材 · 教程)、7tsp(素材 · 教程),可以一次性更改90%的系统图标,用途及效果虽然一样,但两种图标包的使用方法和兼容性不相同,使用前必须查阅相应教程;其中iPack图标包为.exe安装程序,兼容全部Win10系统,但风险较高;7tsp图标包为.7z文件,需要借助7TSP GUI加载,仅兼容Win10 1903之后的系统,风险较低。

\n

很多主题,作者都是搭配好图标包的,有些直接包含在了主题压缩包内,有些是要下载的,以 陶瓷灰 这款Win10主题的图标为例,我们打开主题压缩包:

\n

\"img\"

\n

你能看到里面有一个“图标.url”,双击打开它,即可在浏览器里面加载到图标下载页面,下载后,打开图标压缩包:

\n

\"img\"

\n

里面包含了“7tsp”和“iPack”文件夹,我们选择其中一种即可;再次说明,不论你选择的是哪一种,都必须先看下相应的教程再安装:iPack图标包安装注意事项

\n

修改桌面软件图标

软件图标就是前面提到的像QQ、微信之类的啦:

\n

\"img\"

\n

桌面上的图标分为两类:系统工具图标,如:此电脑、网络、文档、回收站;软件快捷方式图标,如:QQ、微信、360浏览器等…详细方法请参考:桌面图标更改方法

\n

修改任务栏软件图标

\"img\"

\n

任务栏软件图标,就是被你手动固定在任务栏上的那些软件的图标,如上图,详细方法请参考:任务栏软件图标修改方法

\n

\"img\"

\n

\"img\"

\n"},{"date":"2021-09-09T08:41:28.000Z","title":"MDK keil卸载芯片包packs","description":"卸载mdk keil5已经安装过的一些packs","cover":"https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png","abbrlink":"a4c109bd","_content":"\n\n\n\n## MDK Keil卸载packs\n\n比如我要卸载包:STM32F4XXX\n\n1. 打开MDK keil5软件,点击图中箭头所指的选项\n ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201121085508843.png#pic_center)\n\n2. 在左边选择devices,搜索 “stm32”,然后选择\n\n ![image-20210909163924334](https://gitee.com/ajream/images/raw/master/img/20210909163932_image-20210909163924334.png)\n\n3. 重启mdk keil5即可\n\n","source":"_posts/stm32/MDK_Keil卸载已经安装的pack.md","raw":"---\ndate: '2021-09-09 16:41:28'\ntitle: MDK keil卸载芯片包packs\ntags:\n - MDK-keil5\ncategories:\n - 硬件学习\n - stm32\ndescription: 卸载mdk keil5已经安装过的一些packs\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png'\nabbrlink: a4c109bd\n---\n\n\n\n\n## MDK Keil卸载packs\n\n比如我要卸载包:STM32F4XXX\n\n1. 打开MDK keil5软件,点击图中箭头所指的选项\n ![在这里插入图片描述](https://img-blog.csdnimg.cn/20201121085508843.png#pic_center)\n\n2. 在左边选择devices,搜索 “stm32”,然后选择\n\n ![image-20210909163924334](https://gitee.com/ajream/images/raw/master/img/20210909163932_image-20210909163924334.png)\n\n3. 重启mdk keil5即可\n\n","slug":"stm32/MDK_Keil卸载已经安装的pack","published":1,"updated":"2021-09-10T12:48:27.623Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6r9007sakvea9jb34oj","content":"

MDK Keil卸载packs

比如我要卸载包:STM32F4XXX

\n
    \n
  1. 打开MDK keil5软件,点击图中箭头所指的选项
    \"在这里插入图片描述\"

    \n
  2. \n
  3. 在左边选择devices,搜索 “stm32”,然后选择

    \n

    \"image-20210909163924334\"

    \n
  4. \n
  5. 重启mdk keil5即可

    \n
  6. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

MDK Keil卸载packs

比如我要卸载包:STM32F4XXX

\n
    \n
  1. 打开MDK keil5软件,点击图中箭头所指的选项
    \"在这里插入图片描述\"

    \n
  2. \n
  3. 在左边选择devices,搜索 “stm32”,然后选择

    \n

    \"image-20210909163924334\"

    \n
  4. \n
  5. 重启mdk keil5即可

    \n
  6. \n
\n"},{"title":"win10创建系统还原及如何还原","description":"Win10创建系统还原点及还原演示","cover":"https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png","abbrlink":"446faab7","swiper_index":1,"date":"2021-09-05T02:20:22.000Z","_content":"\n\n\n[原文查看](https://zhutix.com/10tutorials/huanyuan/)\n\n## Win10创建系统还原点及还原演示\n\n### 开启系统还原功能\n\n在此电脑右键 – 属性\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-01.png)\n\n在系统属性面板左侧选择系统保护(如下图)\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-02.png)\n\n选择C盘,点击配置(如下图)\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-03.png)\n\n启用系统保护,选择一个使用量(随意)如下图\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-04.png)\n\n### 创建系统还原点\n\n选择C盘,点击创建,输入一个名字,创建即可,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-05.png)\n\n稍等片刻即可创建成功,如下图\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-06.png)\n\n### 正常情况下还原系统\n\n如果你在使用电脑的途中想还原,可以再次打开系统保护界面,点击系统还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-07.png)\n\n选择一个要还原的时间,下一步,即可开始还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-08.png)\n\n### 无法进入系统情况下还原系统\n\n系统出现问题,无法进入的情况下我们可以进入WinRE还原系统:\n\n开机按F8进入WinRE(有的品牌电脑是按F11键或其他按键,请自行尝试或查询)或待系统启动到出现Windows徽标或品牌Logo时,长按电源键强制关机,如此强制关机三次后,系统因无法正常启动就会进入“恢复”界面或“自动修复”界面,点击“查看高级修复选项”或“高级选项”即可进入WinRE。如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-09.png)\n\n进入WinRE后我们进入疑难解答,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-10.png)\n\n高级选项:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-11.png)\n\n进入系统还原:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-12.png)\n\n选择一个要还原的时间,下一步,即可开始还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-13.png)\n\n\n\n### 视频演示\n\nhttp://v.youku.com/v_show/id_XNDM5MjYxMTI2NA==.html","source":"_posts/win10/win10创建系统还原节点.md","raw":"---\ntitle: win10创建系统还原及如何还原\ntags:\n - win10\ncategories: win10\ndescription: Win10创建系统还原点及还原演示\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210905102351_win10_.png'\nabbrlink: 446faab7\nswiper_index: 1\ndate: 2021-09-05 10:20:22\n---\n\n\n\n[原文查看](https://zhutix.com/10tutorials/huanyuan/)\n\n## Win10创建系统还原点及还原演示\n\n### 开启系统还原功能\n\n在此电脑右键 – 属性\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-01.png)\n\n在系统属性面板左侧选择系统保护(如下图)\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-02.png)\n\n选择C盘,点击配置(如下图)\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-03.png)\n\n启用系统保护,选择一个使用量(随意)如下图\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-04.png)\n\n### 创建系统还原点\n\n选择C盘,点击创建,输入一个名字,创建即可,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-05.png)\n\n稍等片刻即可创建成功,如下图\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-06.png)\n\n### 正常情况下还原系统\n\n如果你在使用电脑的途中想还原,可以再次打开系统保护界面,点击系统还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-07.png)\n\n选择一个要还原的时间,下一步,即可开始还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-08.png)\n\n### 无法进入系统情况下还原系统\n\n系统出现问题,无法进入的情况下我们可以进入WinRE还原系统:\n\n开机按F8进入WinRE(有的品牌电脑是按F11键或其他按键,请自行尝试或查询)或待系统启动到出现Windows徽标或品牌Logo时,长按电源键强制关机,如此强制关机三次后,系统因无法正常启动就会进入“恢复”界面或“自动修复”界面,点击“查看高级修复选项”或“高级选项”即可进入WinRE。如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-09.png)\n\n进入WinRE后我们进入疑难解答,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-10.png)\n\n高级选项:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-11.png)\n\n进入系统还原:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-12.png)\n\n选择一个要还原的时间,下一步,即可开始还原,如下图:\n\n![img](https://dl.zhutix.net/2019/10/huanyuan-13.png)\n\n\n\n### 视频演示\n\nhttp://v.youku.com/v_show/id_XNDM5MjYxMTI2NA==.html","slug":"win10/win10创建系统还原节点","published":1,"updated":"2021-09-08T14:45:51.921Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rb007wakve7c6t6uot","content":"

原文查看

\n

Win10创建系统还原点及还原演示

开启系统还原功能

在此电脑右键 – 属性

\n

\"img\"

\n

在系统属性面板左侧选择系统保护(如下图)

\n

\"img\"

\n

选择C盘,点击配置(如下图)

\n

\"img\"

\n

启用系统保护,选择一个使用量(随意)如下图

\n

\"img\"

\n

创建系统还原点

选择C盘,点击创建,输入一个名字,创建即可,如下图:

\n

\"img\"

\n

稍等片刻即可创建成功,如下图

\n

\"img\"

\n

正常情况下还原系统

如果你在使用电脑的途中想还原,可以再次打开系统保护界面,点击系统还原,如下图:

\n

\"img\"

\n

选择一个要还原的时间,下一步,即可开始还原,如下图:

\n

\"img\"

\n

无法进入系统情况下还原系统

系统出现问题,无法进入的情况下我们可以进入WinRE还原系统:

\n

开机按F8进入WinRE(有的品牌电脑是按F11键或其他按键,请自行尝试或查询)或待系统启动到出现Windows徽标或品牌Logo时,长按电源键强制关机,如此强制关机三次后,系统因无法正常启动就会进入“恢复”界面或“自动修复”界面,点击“查看高级修复选项”或“高级选项”即可进入WinRE。如下图:

\n

\"img\"

\n

进入WinRE后我们进入疑难解答,如下图:

\n

\"img\"

\n

高级选项:

\n

\"img\"

\n

进入系统还原:

\n

\"img\"

\n

选择一个要还原的时间,下一步,即可开始还原,如下图:

\n

\"img\"

\n

视频演示

http://v.youku.com/v_show/id_XNDM5MjYxMTI2NA==.html

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

原文查看

\n

Win10创建系统还原点及还原演示

开启系统还原功能

在此电脑右键 – 属性

\n

\"img\"

\n

在系统属性面板左侧选择系统保护(如下图)

\n

\"img\"

\n

选择C盘,点击配置(如下图)

\n

\"img\"

\n

启用系统保护,选择一个使用量(随意)如下图

\n

\"img\"

\n

创建系统还原点

选择C盘,点击创建,输入一个名字,创建即可,如下图:

\n

\"img\"

\n

稍等片刻即可创建成功,如下图

\n

\"img\"

\n

正常情况下还原系统

如果你在使用电脑的途中想还原,可以再次打开系统保护界面,点击系统还原,如下图:

\n

\"img\"

\n

选择一个要还原的时间,下一步,即可开始还原,如下图:

\n

\"img\"

\n

无法进入系统情况下还原系统

系统出现问题,无法进入的情况下我们可以进入WinRE还原系统:

\n

开机按F8进入WinRE(有的品牌电脑是按F11键或其他按键,请自行尝试或查询)或待系统启动到出现Windows徽标或品牌Logo时,长按电源键强制关机,如此强制关机三次后,系统因无法正常启动就会进入“恢复”界面或“自动修复”界面,点击“查看高级修复选项”或“高级选项”即可进入WinRE。如下图:

\n

\"img\"

\n

进入WinRE后我们进入疑难解答,如下图:

\n

\"img\"

\n

高级选项:

\n

\"img\"

\n

进入系统还原:

\n

\"img\"

\n

选择一个要还原的时间,下一步,即可开始还原,如下图:

\n

\"img\"

\n

视频演示

http://v.youku.com/v_show/id_XNDM5MjYxMTI2NA==.html

\n"},{"title":"stm32标准外设库介绍","date":"2021-09-11T06:00:25.000Z","description":"认识stm32标准外设库,目录结构、文件作用","cover":"https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png","abbrlink":"1294a4bd","_content":"\n\n\n\n\n\n\n\n## 固件库介绍\n\n\nSTM32 标准函数库,由 ST 公司针对 STM32 提供的函数接口,即API(Application Program Interface),开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。\n\n\n\n\n\n\n\n\n\n## 下载\n\n建议去[官网](https://www.stmicroelectronics.com.cn/zh/embedded-software/stm32-standard-peripheral-libraries.html?querycriteria=productId=LN1939)搜索下载最新版\n\n![image-20210911141115314](https://gitee.com/ajream/images/raw/master/img/20210911141118_image-20210911141115314.png)\n\n\n\n下载(第一次下载需要输入邮箱,然后会发来一封邮件,点击邮件的链接进行下载)\n\n![image-20210911141332276](https://gitee.com/ajream/images/raw/master/img/20210911141333_image-20210911141332276.png)\n\n\n\n下载完成随便找个位置进行解压即可,后面建立工程时要使用的话再把其中的文件复制到我们自己的工程即可\n\n\n\n## 体系架构\n\n这是解压后固件库的目录结构\n\n| ![image-20210911141948049](https://gitee.com/ajream/images/raw/master/img/20210911141949_image-20210911141948049.png) | ![](https://img-blog.csdnimg.cn/20190302162843226.png) |\n| ------------------------------------------------------------ | ------------------------------------------------------ |\n\n\n\n\n\n\n\n体系架构:\n\n![image-20210911151837136](https://gitee.com/ajream/images/raw/master/img/20210911151840_image-20210911151837136.png)\n\n\n\n1. 用户层\n\n 用户层位于架构的最顶端,包含与用户编程有关的所有文件,`main.c`, `stm32f10x_it.c`, `stm32f10x_it.h`, `stm32f10x_conf.h`, 用户使用固件库进行开发,主要编写`main.c`和`stm32f10x_it.c`\n\n2. CMSIS层\n\n CMSIS层位于体系架构的中间,向下负责与内核和各个外设打交道,向上提供函数接口供用户程序或操作系统调用。CMSIS层主要由设备外设函数和CMSIS核心层构成\n\n ![image-20210911153147123](https://gitee.com/ajream/images/raw/master/img/20210911153148_image-20210911153147123.png)\n\n \n\n - 设备外设函数:由各个芯片产商提供\n\n - misc.c/misc.h:NVIC代码\n \n - stm32f10x_ppp.c/stm32f10x_ppp.h:外设驱动代码\n \n \n \n - CMSIS核心层:包括核内外设访问层(arm公司提供)和设备外设访问层(ST公司提供)\n \n - core_cm3.c/core_cm3.h:Cortex-M3内核通用源文件、头文件\n \n - stm32f10x.h/stm32f10x.c/system_stm32f10x.h等:包含了核外外设寄存器名称、地址、中断向量定义等\n\n\n \"image-20210911161452989\"\n\n3. 微控制器(MCU)层\n\n 微控制器层也叫硬件层,位于架构体系最底层\n\n\n\n","source":"_posts/stm32/stm32标准外设库介绍.md","raw":"---\ntitle: stm32标准外设库介绍\ndate: '2021-09-11 14:00:25'\ntags:\n - stm32\ncategories:\n - 硬件学习\n - stm32\ndescription: 认识stm32标准外设库,目录结构、文件作用\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png'\nabbrlink: 1294a4bd\n---\n\n\n\n\n\n\n\n\n## 固件库介绍\n\n\nSTM32 标准函数库,由 ST 公司针对 STM32 提供的函数接口,即API(Application Program Interface),开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。\n\n\n\n\n\n\n\n\n\n## 下载\n\n建议去[官网](https://www.stmicroelectronics.com.cn/zh/embedded-software/stm32-standard-peripheral-libraries.html?querycriteria=productId=LN1939)搜索下载最新版\n\n![image-20210911141115314](https://gitee.com/ajream/images/raw/master/img/20210911141118_image-20210911141115314.png)\n\n\n\n下载(第一次下载需要输入邮箱,然后会发来一封邮件,点击邮件的链接进行下载)\n\n![image-20210911141332276](https://gitee.com/ajream/images/raw/master/img/20210911141333_image-20210911141332276.png)\n\n\n\n下载完成随便找个位置进行解压即可,后面建立工程时要使用的话再把其中的文件复制到我们自己的工程即可\n\n\n\n## 体系架构\n\n这是解压后固件库的目录结构\n\n| ![image-20210911141948049](https://gitee.com/ajream/images/raw/master/img/20210911141949_image-20210911141948049.png) | ![](https://img-blog.csdnimg.cn/20190302162843226.png) |\n| ------------------------------------------------------------ | ------------------------------------------------------ |\n\n\n\n\n\n\n\n体系架构:\n\n![image-20210911151837136](https://gitee.com/ajream/images/raw/master/img/20210911151840_image-20210911151837136.png)\n\n\n\n1. 用户层\n\n 用户层位于架构的最顶端,包含与用户编程有关的所有文件,`main.c`, `stm32f10x_it.c`, `stm32f10x_it.h`, `stm32f10x_conf.h`, 用户使用固件库进行开发,主要编写`main.c`和`stm32f10x_it.c`\n\n2. CMSIS层\n\n CMSIS层位于体系架构的中间,向下负责与内核和各个外设打交道,向上提供函数接口供用户程序或操作系统调用。CMSIS层主要由设备外设函数和CMSIS核心层构成\n\n ![image-20210911153147123](https://gitee.com/ajream/images/raw/master/img/20210911153148_image-20210911153147123.png)\n\n \n\n - 设备外设函数:由各个芯片产商提供\n\n - misc.c/misc.h:NVIC代码\n \n - stm32f10x_ppp.c/stm32f10x_ppp.h:外设驱动代码\n \n \n \n - CMSIS核心层:包括核内外设访问层(arm公司提供)和设备外设访问层(ST公司提供)\n \n - core_cm3.c/core_cm3.h:Cortex-M3内核通用源文件、头文件\n \n - stm32f10x.h/stm32f10x.c/system_stm32f10x.h等:包含了核外外设寄存器名称、地址、中断向量定义等\n\n\n \"image-20210911161452989\"\n\n3. 微控制器(MCU)层\n\n 微控制器层也叫硬件层,位于架构体系最底层\n\n\n\n","slug":"stm32/stm32标准外设库介绍","published":1,"updated":"2021-09-12T03:38:57.863Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rc007yakve8853hjrp","content":"

固件库介绍

STM32 标准函数库,由 ST 公司针对 STM32 提供的函数接口,即API(Application Program Interface),开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。

\n

下载

建议去官网搜索下载最新版

\n

\"image-20210911141115314\"

\n

下载(第一次下载需要输入邮箱,然后会发来一封邮件,点击邮件的链接进行下载)

\n

\"image-20210911141332276\"

\n

下载完成随便找个位置进行解压即可,后面建立工程时要使用的话再把其中的文件复制到我们自己的工程即可

\n

体系架构

这是解压后固件库的目录结构

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n
\"image-20210911141948049\"\"\"
\n
\n

体系架构:

\n

\"image-20210911151837136\"

\n
    \n
  1. 用户层

    \n

    用户层位于架构的最顶端,包含与用户编程有关的所有文件,main.c, stm32f10x_it.c, stm32f10x_it.h, stm32f10x_conf.h, 用户使用固件库进行开发,主要编写main.cstm32f10x_it.c

    \n
  2. \n
  3. CMSIS层

    \n

    CMSIS层位于体系架构的中间,向下负责与内核和各个外设打交道,向上提供函数接口供用户程序或操作系统调用。CMSIS层主要由设备外设函数和CMSIS核心层构成

    \n

    \"image-20210911153147123\"

    \n
  4. \n
\n
    \n
  • 设备外设函数:由各个芯片产商提供

    \n
      \n
    • misc.c/misc.h:NVIC代码

      \n
    • \n
    • stm32f10x_ppp.c/stm32f10x_ppp.h:外设驱动代码

      \n
    • \n
    \n
  • \n
\n
    \n
  • CMSIS核心层:包括核内外设访问层(arm公司提供)和设备外设访问层(ST公司提供)

    \n
      \n
    • core_cm3.c/core_cm3.h:Cortex-M3内核通用源文件、头文件

      \n
    • \n
    • stm32f10x.h/stm32f10x.c/system_stm32f10x.h等:包含了核外外设寄存器名称、地址、中断向量定义等

      \n
    • \n
    \n
  • \n
\n

\"image-20210911161452989\"

\n
    \n
  1. 微控制器(MCU)层

    \n

    微控制器层也叫硬件层,位于架构体系最底层

    \n
  2. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

固件库介绍

STM32 标准函数库,由 ST 公司针对 STM32 提供的函数接口,即API(Application Program Interface),开发者可调用这些函数接口来配置STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。

\n

下载

建议去官网搜索下载最新版

\n

\"image-20210911141115314\"

\n

下载(第一次下载需要输入邮箱,然后会发来一封邮件,点击邮件的链接进行下载)

\n

\"image-20210911141332276\"

\n

下载完成随便找个位置进行解压即可,后面建立工程时要使用的话再把其中的文件复制到我们自己的工程即可

\n

体系架构

这是解压后固件库的目录结构

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n
\"image-20210911141948049\"\"\"
\n
\n

体系架构:

\n

\"image-20210911151837136\"

\n
    \n
  1. 用户层

    \n

    用户层位于架构的最顶端,包含与用户编程有关的所有文件,main.c, stm32f10x_it.c, stm32f10x_it.h, stm32f10x_conf.h, 用户使用固件库进行开发,主要编写main.cstm32f10x_it.c

    \n
  2. \n
  3. CMSIS层

    \n

    CMSIS层位于体系架构的中间,向下负责与内核和各个外设打交道,向上提供函数接口供用户程序或操作系统调用。CMSIS层主要由设备外设函数和CMSIS核心层构成

    \n

    \"image-20210911153147123\"

    \n
  4. \n
\n
    \n
  • 设备外设函数:由各个芯片产商提供

    \n
      \n
    • misc.c/misc.h:NVIC代码

      \n
    • \n
    • stm32f10x_ppp.c/stm32f10x_ppp.h:外设驱动代码

      \n
    • \n
    \n
  • \n
\n
    \n
  • CMSIS核心层:包括核内外设访问层(arm公司提供)和设备外设访问层(ST公司提供)

    \n
      \n
    • core_cm3.c/core_cm3.h:Cortex-M3内核通用源文件、头文件

      \n
    • \n
    • stm32f10x.h/stm32f10x.c/system_stm32f10x.h等:包含了核外外设寄存器名称、地址、中断向量定义等

      \n
    • \n
    \n
  • \n
\n

\"image-20210911161452989\"

\n
    \n
  1. 微控制器(MCU)层

    \n

    微控制器层也叫硬件层,位于架构体系最底层

    \n
  2. \n
\n"},{"date":"2021-09-10T08:41:28.000Z","title":"MDK keil新建stm32工程","description":"一步步带你使用mdk keil建立第一个stm32关于库函数(不是寄存器)的project","cover":"https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png","abbrlink":"190b30be","_content":"\n\n\n\n\n\n\n\n\n## 工程结构建立\n\n\n\n### 初始化工程目录结构\n\n首先创建一个文件夹test, 用于存放该工程,然后在该文件夹下创建以下几个文件夹(目的是让工程管理起来更有条理):\n\n- lib: 用于存放库文件(像Libraries、Lib等文件名字也是可以的,根据自己的风格,最好是能\"见名知义\")\n - CMSIS: 存放内核核心库文件\n - STM32F10x_StdPeriph_Driver: 是STM32Fl0x标准外设驱动库函数目录,存放STM32F10x微控制器的外设驱动(这个文件夹可以不建立,等下直接把库的拷贝过来)\n- obj: 用于存放程序编译输出的各种文件\n- user: 用于存放main.c等用户自己的文件\n\n\n\n### 添加相关文件\n\n#### 固件库说明\n\n{%folding blue,展开%}\n\n首先要去官网或其他渠道下载STM32固件库,这个库包含了我们创建要用到的所有文件,其包含的文件如下\n\n![image-20210910194646565](https://gitee.com/ajream/images/raw/master/img/20210910194652_image-20210910194646565.png)\n\n\n\n【Libraries】文件夹存放STM32F10x开发要用到的各种库函数和启动文件,包括`CMSIS`和`STM32F10x_StdPeriph_Driver`两个子文件夹:\n\n1. 【CMSIS】文件夹是内核库核心文件夹,包括CoreSupport和DeviceSupport等两个文件夹 \n\n CoreSupport文件夹包括Cortcx-M3内核通用源文件`core_cm3.c`和头文件`core_cm3.h`\n \n2. 【DeviceSupport】文件夹STM32F0x头文件`stm32f10x.h`和系统初始化文件`system_stm32f10x.c`【STM32F10x_StdPeriph_Driver】文件夹是STM32Fl0x标准外设驱动库函数目录,包括了所有STM32F10x微控制器的外设驱动\n\n![image-20210910195021702](https://gitee.com/ajream/images/raw/master/img/20210910195026_image-20210910195021702.png)\n\n\n\n【Project】文件夹对应STM32F10x标准外设库体系架构中的用户层,用来存放ST官方提供的STM32F10x工程模板和外设驱动示例\n\n【Utilities】文件夹用于存放ST官方评估板的BSP(Board Support Package, 板级支持包)和额外的第三方固件\n\n\n\n{%endfolding%}\n\n\n\n#### 添加库文件到自己的模板\n\n##### lib目录-1\n\n在库文件找到下面的文件拷贝到自己刚建立的`lib/CMSIS`目录\n\n添加完成后 `lib/CMSIS`目录是这样:\n\n![image-20210910201435838](https://gitee.com/ajream/images/raw/master/img/20210910201440_image-20210910201435838.png)\n\n这些文件去哪里找:在库文件的 【Libraries】目录下\n\n内核通用源文件\n\n![image-20210910200517197](https://gitee.com/ajream/images/raw/master/img/20210910200521_image-20210910200517197.png)\n\n启动文件,只需要红框圈起来的那个,对应 stm32f103ZE开发板芯片\n\n![image-20210910200757380](https://gitee.com/ajream/images/raw/master/img/20210910200802_image-20210910200757380.png)\n\n系统初始化文件\n\n![image-20210910201253535](https://gitee.com/ajream/images/raw/master/img/20210910201256_image-20210910201253535.png)\n\n##### `lib`目录-2\n\n去固件库的【Libraries】文件夹下【如下图】把文件夹`STM32F10x_StdPeriph_Driver`拷贝到自己建立的 `lib` 目录下\n\n![image-20210910201957385](https://gitee.com/ajream/images/raw/master/img/20210910202001_image-20210910201957385.png)\n\n##### `user`目录\n\n这是添加完成后的`user`目录:\n\n![image-20210910202442964](https://gitee.com/ajream/images/raw/master/img/20210910202446_image-20210910202442964.png)\n\n这些文件在哪里找:\n\n- main.c: 可以自己新建一个空的`main.c`文件,也可以去官方模板那里拷贝过来(官方模板在 `/Project/STM32F10x_StdPeriph_Template/`目录下, 下面第1幅图)\n- 最下面的三个`stm32f10x_conf.h`、`stm32f10x_it.c`、`stm32f10x_it.h`: 去官方模板拷贝\n- 第二个stm32f10x.h: 在库的`Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x`路径下找(下面第2幅图)\n\n\n\n![image-20210910202705273](https://gitee.com/ajream/images/raw/master/img/20210910202708_image-20210910202705273.png)\n\n\n\n![image-20210910203037259](https://gitee.com/ajream/images/raw/master/img/20210910203042_image-20210910203037259.png)\n\n\n\n{%note info flat%}\n\n注意,以上步骤只是在磁盘上建立了这么一个目录结构,keil还不知道有这个工程,所以我们要在keil新建一个工程,然后把这个工程与存放在磁盘上的目录(还不能叫工程)进行关联,这样才能形成一个完整结构的工程。\n\n{%endnote%}\n\n### 打开mdk keil新建stm32工程\n\n这个步骤是为了在keil中新建一个工程与存放在磁盘上的目录(还不能叫工程)进行关联映射,所以有2步:1-新建工程, 2-进行关联\n\n#### 新建工程\n\n注意新建工程时,有几个步骤:\n\n1. 工程名,任意(根据自己的命名风格,不能有中文)\n2. 选择芯片,这里使用 `STM32F103ZE`\n3. 选择库,这里先不选,后面再选,直接跳过\n\n{%folding blue, 具体步骤%}\n\n点击 `new uVision Project`\n\n![image-20210910210845401](https://gitee.com/ajream/images/raw/master/img/20210910210853_image-20210910210845401.png)\n\n自己命名,选择保存;\n\n选择芯片 `STM32F103ZE`\n\n![image-20210910211314033](https://gitee.com/ajream/images/raw/master/img/20210910211318_image-20210910211314033.png)\n\n\n\n选择库,直接跳过,后面再选\n\n![image-20210910211418672](https://gitee.com/ajream/images/raw/master/img/20210910211421_image-20210910211418672.png)\n\n完成\n\n![image-20210910211508362](https://gitee.com/ajream/images/raw/master/img/20210910211512_image-20210910211508362.png)\n\n{%endfolding%}\n\n\n\n#### 完善工程结构\n\n这里要把这个工程与磁盘上的目录和文件进行映射关联\n\n选择工程管理,如下:\n\n![image-20210910212307092](https://gitee.com/ajream/images/raw/master/img/20210910212322_image-20210910212307092.png)\n\n\n\n依次新建这几个目录:\n\n- user: 存放用户代码文件\n- startup: 存放启动文件\n- CMSIS: 内核库文件\n- StdPeriph_Driver: 外设库文件\n\n![image-20210910212703182](https://gitee.com/ajream/images/raw/master/img/20210910212706_image-20210910212703182.png)\n\n\n\n分别往这几个目录添加文件:\n\n![image-20210910214210061](https://gitee.com/ajream/images/raw/master/img/20210910214215_image-20210910214210061.png)\n\n\n\nuser(从 `/user/`目录下选择)\n\n- main.c\n- stm32f10x_it.c\n\nstartup(从 `/lib/CMSIS/` 目录下选择)\n\n- startup_stm32f10x_hd.s(注意添加时没显示出来要选择 文件类型为 \"`All files(*.*)`\")\n\nCMSIS(从 `/lib/CMSIS/`目录下选择)\n\n- core_cm3.c\n- system_stm32f10x.c\n\nStdPeriph_Driver(从`/lib/STM32F10x_StdPeriph_Driver/src/`目录下选择,可以选择全部,也可以只选择下面两个,根据自己需要,下面这2个一般都会用到,所以都添加进来)\n\n- stm32f10x_rcc.c\n- stm32f10x_gpio.c\n\n\n\n添加完成如下:\n\n![image-20210910214412880](https://gitee.com/ajream/images/raw/master/img/20210910214416_image-20210910214412880.png)\n\n\n\n{%note info flat%}\n\n到此工程结构已经建立完成,可以在main.c文件下写代码了,但是现在还不能编译,因为编译需要的头文件还没包含进来,以及编译时的其它一些配置还没完成\n\n\n\n{%endnote%}\n\n\n\n\n\n## 编译配置\n\n选择这个魔法棒\n\n![image-20210910215321021](https://gitee.com/ajream/images/raw/master/img/20210910215324_image-20210910215321021.png)\n\n\n\n配置Target项\n\n![image-20210910215245013](https://gitee.com/ajream/images/raw/master/img/20210910215248_image-20210910215245013.png)\n\n\n\n配置output项\n\n![image-20210910220000653](https://gitee.com/ajream/images/raw/master/img/20210910220004_image-20210910220000653.png)\n\n\n\n\n\n配置 listing项\n\n![image-20210910220055189](https://gitee.com/ajream/images/raw/master/img/20210910220059_image-20210910220055189.png)\n\n\n\n配置 C/C++ 项,目的是为了把头文件添加进来\n\n下图的第2步添加的是:`USE_STDPERIPH_DRIVER,STM32F10X_HD`(注意中间有个英文逗号),表示使用外部库函数驱动\n\n![image-20210910220625228](https://gitee.com/ajream/images/raw/master/img/20210910220629_image-20210910220625228.png)\n\n\n\n\n\n配置Debug项:ARM仿真器选项配置(手中有开发板可以先通过ARM仿真器连接电脑)\n\n![image-20210910221520551](https://gitee.com/ajream/images/raw/master/img/20210910221523_image-20210910221520551.png)\n\n\n\n下载配置(在上一步的Debugger配置中:Debugger->Settings->FlashDownload)\n\n![image-20210910221912505](https://gitee.com/ajream/images/raw/master/img/20210910221917_image-20210910221912505.png)\n\n\n\n\n\n{%note info flat%}\n\n到此所有配置完成,选择ok,可以愉快的写代码了\n\n\n\n{%endnote%}\n\n\n\n\n\n## 测试\n\n打开main.c文件,删除原有代码(如果有的话),添加如下代码:\n\n\n\n```c\n#include \"stm32f10x.h\"\n\nint main(){\n\t\n\twhile(1){\n \n\t}\n}\n\n```\n\n\n\n编译运行,没有报错,完美。。。。\n\n![image-20210910222416127](https://gitee.com/ajream/images/raw/master/img/20210910222420_image-20210910222416127.png)\n\n\n\n\n\n","source":"_posts/stm32/keil新建库函数版本的project.md","raw":"---\ndate: '2021-09-10 16:41:28'\ntitle: MDK keil新建stm32工程\ntags:\n - stm32\ncategories:\n - 硬件学习\n - stm32\ndescription: 一步步带你使用mdk keil建立第一个stm32关于库函数(不是寄存器)的project\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png'\nabbrlink: 190b30be\n---\n\n\n\n\n\n\n\n\n\n## 工程结构建立\n\n\n\n### 初始化工程目录结构\n\n首先创建一个文件夹test, 用于存放该工程,然后在该文件夹下创建以下几个文件夹(目的是让工程管理起来更有条理):\n\n- lib: 用于存放库文件(像Libraries、Lib等文件名字也是可以的,根据自己的风格,最好是能\"见名知义\")\n - CMSIS: 存放内核核心库文件\n - STM32F10x_StdPeriph_Driver: 是STM32Fl0x标准外设驱动库函数目录,存放STM32F10x微控制器的外设驱动(这个文件夹可以不建立,等下直接把库的拷贝过来)\n- obj: 用于存放程序编译输出的各种文件\n- user: 用于存放main.c等用户自己的文件\n\n\n\n### 添加相关文件\n\n#### 固件库说明\n\n{%folding blue,展开%}\n\n首先要去官网或其他渠道下载STM32固件库,这个库包含了我们创建要用到的所有文件,其包含的文件如下\n\n![image-20210910194646565](https://gitee.com/ajream/images/raw/master/img/20210910194652_image-20210910194646565.png)\n\n\n\n【Libraries】文件夹存放STM32F10x开发要用到的各种库函数和启动文件,包括`CMSIS`和`STM32F10x_StdPeriph_Driver`两个子文件夹:\n\n1. 【CMSIS】文件夹是内核库核心文件夹,包括CoreSupport和DeviceSupport等两个文件夹 \n\n CoreSupport文件夹包括Cortcx-M3内核通用源文件`core_cm3.c`和头文件`core_cm3.h`\n \n2. 【DeviceSupport】文件夹STM32F0x头文件`stm32f10x.h`和系统初始化文件`system_stm32f10x.c`【STM32F10x_StdPeriph_Driver】文件夹是STM32Fl0x标准外设驱动库函数目录,包括了所有STM32F10x微控制器的外设驱动\n\n![image-20210910195021702](https://gitee.com/ajream/images/raw/master/img/20210910195026_image-20210910195021702.png)\n\n\n\n【Project】文件夹对应STM32F10x标准外设库体系架构中的用户层,用来存放ST官方提供的STM32F10x工程模板和外设驱动示例\n\n【Utilities】文件夹用于存放ST官方评估板的BSP(Board Support Package, 板级支持包)和额外的第三方固件\n\n\n\n{%endfolding%}\n\n\n\n#### 添加库文件到自己的模板\n\n##### lib目录-1\n\n在库文件找到下面的文件拷贝到自己刚建立的`lib/CMSIS`目录\n\n添加完成后 `lib/CMSIS`目录是这样:\n\n![image-20210910201435838](https://gitee.com/ajream/images/raw/master/img/20210910201440_image-20210910201435838.png)\n\n这些文件去哪里找:在库文件的 【Libraries】目录下\n\n内核通用源文件\n\n![image-20210910200517197](https://gitee.com/ajream/images/raw/master/img/20210910200521_image-20210910200517197.png)\n\n启动文件,只需要红框圈起来的那个,对应 stm32f103ZE开发板芯片\n\n![image-20210910200757380](https://gitee.com/ajream/images/raw/master/img/20210910200802_image-20210910200757380.png)\n\n系统初始化文件\n\n![image-20210910201253535](https://gitee.com/ajream/images/raw/master/img/20210910201256_image-20210910201253535.png)\n\n##### `lib`目录-2\n\n去固件库的【Libraries】文件夹下【如下图】把文件夹`STM32F10x_StdPeriph_Driver`拷贝到自己建立的 `lib` 目录下\n\n![image-20210910201957385](https://gitee.com/ajream/images/raw/master/img/20210910202001_image-20210910201957385.png)\n\n##### `user`目录\n\n这是添加完成后的`user`目录:\n\n![image-20210910202442964](https://gitee.com/ajream/images/raw/master/img/20210910202446_image-20210910202442964.png)\n\n这些文件在哪里找:\n\n- main.c: 可以自己新建一个空的`main.c`文件,也可以去官方模板那里拷贝过来(官方模板在 `/Project/STM32F10x_StdPeriph_Template/`目录下, 下面第1幅图)\n- 最下面的三个`stm32f10x_conf.h`、`stm32f10x_it.c`、`stm32f10x_it.h`: 去官方模板拷贝\n- 第二个stm32f10x.h: 在库的`Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x`路径下找(下面第2幅图)\n\n\n\n![image-20210910202705273](https://gitee.com/ajream/images/raw/master/img/20210910202708_image-20210910202705273.png)\n\n\n\n![image-20210910203037259](https://gitee.com/ajream/images/raw/master/img/20210910203042_image-20210910203037259.png)\n\n\n\n{%note info flat%}\n\n注意,以上步骤只是在磁盘上建立了这么一个目录结构,keil还不知道有这个工程,所以我们要在keil新建一个工程,然后把这个工程与存放在磁盘上的目录(还不能叫工程)进行关联,这样才能形成一个完整结构的工程。\n\n{%endnote%}\n\n### 打开mdk keil新建stm32工程\n\n这个步骤是为了在keil中新建一个工程与存放在磁盘上的目录(还不能叫工程)进行关联映射,所以有2步:1-新建工程, 2-进行关联\n\n#### 新建工程\n\n注意新建工程时,有几个步骤:\n\n1. 工程名,任意(根据自己的命名风格,不能有中文)\n2. 选择芯片,这里使用 `STM32F103ZE`\n3. 选择库,这里先不选,后面再选,直接跳过\n\n{%folding blue, 具体步骤%}\n\n点击 `new uVision Project`\n\n![image-20210910210845401](https://gitee.com/ajream/images/raw/master/img/20210910210853_image-20210910210845401.png)\n\n自己命名,选择保存;\n\n选择芯片 `STM32F103ZE`\n\n![image-20210910211314033](https://gitee.com/ajream/images/raw/master/img/20210910211318_image-20210910211314033.png)\n\n\n\n选择库,直接跳过,后面再选\n\n![image-20210910211418672](https://gitee.com/ajream/images/raw/master/img/20210910211421_image-20210910211418672.png)\n\n完成\n\n![image-20210910211508362](https://gitee.com/ajream/images/raw/master/img/20210910211512_image-20210910211508362.png)\n\n{%endfolding%}\n\n\n\n#### 完善工程结构\n\n这里要把这个工程与磁盘上的目录和文件进行映射关联\n\n选择工程管理,如下:\n\n![image-20210910212307092](https://gitee.com/ajream/images/raw/master/img/20210910212322_image-20210910212307092.png)\n\n\n\n依次新建这几个目录:\n\n- user: 存放用户代码文件\n- startup: 存放启动文件\n- CMSIS: 内核库文件\n- StdPeriph_Driver: 外设库文件\n\n![image-20210910212703182](https://gitee.com/ajream/images/raw/master/img/20210910212706_image-20210910212703182.png)\n\n\n\n分别往这几个目录添加文件:\n\n![image-20210910214210061](https://gitee.com/ajream/images/raw/master/img/20210910214215_image-20210910214210061.png)\n\n\n\nuser(从 `/user/`目录下选择)\n\n- main.c\n- stm32f10x_it.c\n\nstartup(从 `/lib/CMSIS/` 目录下选择)\n\n- startup_stm32f10x_hd.s(注意添加时没显示出来要选择 文件类型为 \"`All files(*.*)`\")\n\nCMSIS(从 `/lib/CMSIS/`目录下选择)\n\n- core_cm3.c\n- system_stm32f10x.c\n\nStdPeriph_Driver(从`/lib/STM32F10x_StdPeriph_Driver/src/`目录下选择,可以选择全部,也可以只选择下面两个,根据自己需要,下面这2个一般都会用到,所以都添加进来)\n\n- stm32f10x_rcc.c\n- stm32f10x_gpio.c\n\n\n\n添加完成如下:\n\n![image-20210910214412880](https://gitee.com/ajream/images/raw/master/img/20210910214416_image-20210910214412880.png)\n\n\n\n{%note info flat%}\n\n到此工程结构已经建立完成,可以在main.c文件下写代码了,但是现在还不能编译,因为编译需要的头文件还没包含进来,以及编译时的其它一些配置还没完成\n\n\n\n{%endnote%}\n\n\n\n\n\n## 编译配置\n\n选择这个魔法棒\n\n![image-20210910215321021](https://gitee.com/ajream/images/raw/master/img/20210910215324_image-20210910215321021.png)\n\n\n\n配置Target项\n\n![image-20210910215245013](https://gitee.com/ajream/images/raw/master/img/20210910215248_image-20210910215245013.png)\n\n\n\n配置output项\n\n![image-20210910220000653](https://gitee.com/ajream/images/raw/master/img/20210910220004_image-20210910220000653.png)\n\n\n\n\n\n配置 listing项\n\n![image-20210910220055189](https://gitee.com/ajream/images/raw/master/img/20210910220059_image-20210910220055189.png)\n\n\n\n配置 C/C++ 项,目的是为了把头文件添加进来\n\n下图的第2步添加的是:`USE_STDPERIPH_DRIVER,STM32F10X_HD`(注意中间有个英文逗号),表示使用外部库函数驱动\n\n![image-20210910220625228](https://gitee.com/ajream/images/raw/master/img/20210910220629_image-20210910220625228.png)\n\n\n\n\n\n配置Debug项:ARM仿真器选项配置(手中有开发板可以先通过ARM仿真器连接电脑)\n\n![image-20210910221520551](https://gitee.com/ajream/images/raw/master/img/20210910221523_image-20210910221520551.png)\n\n\n\n下载配置(在上一步的Debugger配置中:Debugger->Settings->FlashDownload)\n\n![image-20210910221912505](https://gitee.com/ajream/images/raw/master/img/20210910221917_image-20210910221912505.png)\n\n\n\n\n\n{%note info flat%}\n\n到此所有配置完成,选择ok,可以愉快的写代码了\n\n\n\n{%endnote%}\n\n\n\n\n\n## 测试\n\n打开main.c文件,删除原有代码(如果有的话),添加如下代码:\n\n\n\n```c\n#include \"stm32f10x.h\"\n\nint main(){\n\t\n\twhile(1){\n \n\t}\n}\n\n```\n\n\n\n编译运行,没有报错,完美。。。。\n\n![image-20210910222416127](https://gitee.com/ajream/images/raw/master/img/20210910222420_image-20210910222416127.png)\n\n\n\n\n\n","slug":"stm32/keil新建库函数版本的project","published":1,"updated":"2021-09-10T14:47:50.686Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rd0083akve82iv1jsm","content":"

工程结构建立

初始化工程目录结构

首先创建一个文件夹test, 用于存放该工程,然后在该文件夹下创建以下几个文件夹(目的是让工程管理起来更有条理):

\n
    \n
  • lib: 用于存放库文件(像Libraries、Lib等文件名字也是可以的,根据自己的风格,最好是能”见名知义”)
      \n
    • CMSIS: 存放内核核心库文件
    • \n
    • STM32F10x_StdPeriph_Driver: 是STM32Fl0x标准外设驱动库函数目录,存放STM32F10x微控制器的外设驱动(这个文件夹可以不建立,等下直接把库的拷贝过来)
    • \n
    \n
  • \n
  • obj: 用于存放程序编译输出的各种文件
  • \n
  • user: 用于存放main.c等用户自己的文件
  • \n
\n

添加相关文件

固件库说明

展开 \n
\n

首先要去官网或其他渠道下载STM32固件库,这个库包含了我们创建要用到的所有文件,其包含的文件如下

\"image-20210910194646565\"

【Libraries】文件夹存放STM32F10x开发要用到的各种库函数和启动文件,包括CMSISSTM32F10x_StdPeriph_Driver两个子文件夹:

  1. 【CMSIS】文件夹是内核库核心文件夹,包括CoreSupport和DeviceSupport等两个文件夹

    CoreSupport文件夹包括Cortcx-M3内核通用源文件core_cm3.c和头文件core_cm3.h

  2. 【DeviceSupport】文件夹STM32F0x头文件stm32f10x.h和系统初始化文件system_stm32f10x.c【STM32F10x_StdPeriph_Driver】文件夹是STM32Fl0x标准外设驱动库函数目录,包括了所有STM32F10x微控制器的外设驱动

\"image-20210910195021702\"

【Project】文件夹对应STM32F10x标准外设库体系架构中的用户层,用来存放ST官方提供的STM32F10x工程模板和外设驱动示例

【Utilities】文件夹用于存放ST官方评估板的BSP(Board Support Package, 板级支持包)和额外的第三方固件

\n
\n
\n

添加库文件到自己的模板

lib目录-1

在库文件找到下面的文件拷贝到自己刚建立的lib/CMSIS目录

\n

添加完成后 lib/CMSIS目录是这样:

\n

\"image-20210910201435838\"

\n

这些文件去哪里找:在库文件的 【Libraries】目录下

\n

内核通用源文件

\n

\"image-20210910200517197\"

\n

启动文件,只需要红框圈起来的那个,对应 stm32f103ZE开发板芯片

\n

\"image-20210910200757380\"

\n

系统初始化文件

\n

\"image-20210910201253535\"

\n
lib目录-2

去固件库的【Libraries】文件夹下【如下图】把文件夹STM32F10x_StdPeriph_Driver拷贝到自己建立的 lib 目录下

\n

\"image-20210910201957385\"

\n
user目录

这是添加完成后的user目录:

\n

\"image-20210910202442964\"

\n

这些文件在哪里找:

\n
    \n
  • main.c: 可以自己新建一个空的main.c文件,也可以去官方模板那里拷贝过来(官方模板在 /Project/STM32F10x_StdPeriph_Template/目录下, 下面第1幅图)
  • \n
  • 最下面的三个stm32f10x_conf.hstm32f10x_it.cstm32f10x_it.h: 去官方模板拷贝
  • \n
  • 第二个stm32f10x.h: 在库的Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x路径下找(下面第2幅图)
  • \n
\n

\"image-20210910202705273\"

\n

\"image-20210910203037259\"

\n

注意,以上步骤只是在磁盘上建立了这么一个目录结构,keil还不知道有这个工程,所以我们要在keil新建一个工程,然后把这个工程与存放在磁盘上的目录(还不能叫工程)进行关联,这样才能形成一个完整结构的工程。

\n
\n

打开mdk keil新建stm32工程

这个步骤是为了在keil中新建一个工程与存放在磁盘上的目录(还不能叫工程)进行关联映射,所以有2步:1-新建工程, 2-进行关联

\n

新建工程

注意新建工程时,有几个步骤:

\n
    \n
  1. 工程名,任意(根据自己的命名风格,不能有中文)
  2. \n
  3. 选择芯片,这里使用 STM32F103ZE
  4. \n
  5. 选择库,这里先不选,后面再选,直接跳过
  6. \n
\n
具体步骤 \n
\n

点击 new uVision Project

\"image-20210910210845401\"

自己命名,选择保存;

选择芯片 STM32F103ZE

\"image-20210910211314033\"

选择库,直接跳过,后面再选

\"image-20210910211418672\"

完成

\"image-20210910211508362\"

\n
\n
\n

完善工程结构

这里要把这个工程与磁盘上的目录和文件进行映射关联

\n

选择工程管理,如下:

\n

\"image-20210910212307092\"

\n

依次新建这几个目录:

\n
    \n
  • user: 存放用户代码文件
  • \n
  • startup: 存放启动文件
  • \n
  • CMSIS: 内核库文件
  • \n
  • StdPeriph_Driver: 外设库文件
  • \n
\n

\"image-20210910212703182\"

\n

分别往这几个目录添加文件:

\n

\"image-20210910214210061\"

\n

user(从 /user/目录下选择)

\n
    \n
  • main.c
  • \n
  • stm32f10x_it.c
  • \n
\n

startup(从 /lib/CMSIS/ 目录下选择)

\n
    \n
  • startup_stm32f10x_hd.s(注意添加时没显示出来要选择 文件类型为 “All files(*.*)“)
  • \n
\n

CMSIS(从 /lib/CMSIS/目录下选择)

\n
    \n
  • core_cm3.c
  • \n
  • system_stm32f10x.c
  • \n
\n

StdPeriph_Driver(从/lib/STM32F10x_StdPeriph_Driver/src/目录下选择,可以选择全部,也可以只选择下面两个,根据自己需要,下面这2个一般都会用到,所以都添加进来)

\n
    \n
  • stm32f10x_rcc.c
  • \n
  • stm32f10x_gpio.c
  • \n
\n

添加完成如下:

\n

\"image-20210910214412880\"

\n

到此工程结构已经建立完成,可以在main.c文件下写代码了,但是现在还不能编译,因为编译需要的头文件还没包含进来,以及编译时的其它一些配置还没完成

\n
\n

编译配置

选择这个魔法棒

\n

\"image-20210910215321021\"

\n

配置Target项

\n

\"image-20210910215245013\"

\n

配置output项

\n

\"image-20210910220000653\"

\n

配置 listing项

\n

\"image-20210910220055189\"

\n

配置 C/C++ 项,目的是为了把头文件添加进来

\n

下图的第2步添加的是:USE_STDPERIPH_DRIVER,STM32F10X_HD(注意中间有个英文逗号),表示使用外部库函数驱动

\n

\"image-20210910220625228\"

\n

配置Debug项:ARM仿真器选项配置(手中有开发板可以先通过ARM仿真器连接电脑)

\n

\"image-20210910221520551\"

\n

下载配置(在上一步的Debugger配置中:Debugger->Settings->FlashDownload)

\n

\"image-20210910221912505\"

\n

到此所有配置完成,选择ok,可以愉快的写代码了

\n
\n

测试

打开main.c文件,删除原有代码(如果有的话),添加如下代码:

\n
1
2
3
4
5
6
7
8
9
#include "stm32f10x.h"

int main(){
\t
\twhile(1){

\t}
}

\n

编译运行,没有报错,完美。。。。

\n

\"image-20210910222416127\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

工程结构建立

初始化工程目录结构

首先创建一个文件夹test, 用于存放该工程,然后在该文件夹下创建以下几个文件夹(目的是让工程管理起来更有条理):

\n
    \n
  • lib: 用于存放库文件(像Libraries、Lib等文件名字也是可以的,根据自己的风格,最好是能”见名知义”)
      \n
    • CMSIS: 存放内核核心库文件
    • \n
    • STM32F10x_StdPeriph_Driver: 是STM32Fl0x标准外设驱动库函数目录,存放STM32F10x微控制器的外设驱动(这个文件夹可以不建立,等下直接把库的拷贝过来)
    • \n
    \n
  • \n
  • obj: 用于存放程序编译输出的各种文件
  • \n
  • user: 用于存放main.c等用户自己的文件
  • \n
\n

添加相关文件

固件库说明

展开 \n
\n

首先要去官网或其他渠道下载STM32固件库,这个库包含了我们创建要用到的所有文件,其包含的文件如下

\"image-20210910194646565\"

【Libraries】文件夹存放STM32F10x开发要用到的各种库函数和启动文件,包括CMSISSTM32F10x_StdPeriph_Driver两个子文件夹:

  1. 【CMSIS】文件夹是内核库核心文件夹,包括CoreSupport和DeviceSupport等两个文件夹

    CoreSupport文件夹包括Cortcx-M3内核通用源文件core_cm3.c和头文件core_cm3.h

  2. 【DeviceSupport】文件夹STM32F0x头文件stm32f10x.h和系统初始化文件system_stm32f10x.c【STM32F10x_StdPeriph_Driver】文件夹是STM32Fl0x标准外设驱动库函数目录,包括了所有STM32F10x微控制器的外设驱动

\"image-20210910195021702\"

【Project】文件夹对应STM32F10x标准外设库体系架构中的用户层,用来存放ST官方提供的STM32F10x工程模板和外设驱动示例

【Utilities】文件夹用于存放ST官方评估板的BSP(Board Support Package, 板级支持包)和额外的第三方固件

\n
\n
\n

添加库文件到自己的模板

lib目录-1

在库文件找到下面的文件拷贝到自己刚建立的lib/CMSIS目录

\n

添加完成后 lib/CMSIS目录是这样:

\n

\"image-20210910201435838\"

\n

这些文件去哪里找:在库文件的 【Libraries】目录下

\n

内核通用源文件

\n

\"image-20210910200517197\"

\n

启动文件,只需要红框圈起来的那个,对应 stm32f103ZE开发板芯片

\n

\"image-20210910200757380\"

\n

系统初始化文件

\n

\"image-20210910201253535\"

\n
lib目录-2

去固件库的【Libraries】文件夹下【如下图】把文件夹STM32F10x_StdPeriph_Driver拷贝到自己建立的 lib 目录下

\n

\"image-20210910201957385\"

\n
user目录

这是添加完成后的user目录:

\n

\"image-20210910202442964\"

\n

这些文件在哪里找:

\n
    \n
  • main.c: 可以自己新建一个空的main.c文件,也可以去官方模板那里拷贝过来(官方模板在 /Project/STM32F10x_StdPeriph_Template/目录下, 下面第1幅图)
  • \n
  • 最下面的三个stm32f10x_conf.hstm32f10x_it.cstm32f10x_it.h: 去官方模板拷贝
  • \n
  • 第二个stm32f10x.h: 在库的Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x路径下找(下面第2幅图)
  • \n
\n

\"image-20210910202705273\"

\n

\"image-20210910203037259\"

\n

注意,以上步骤只是在磁盘上建立了这么一个目录结构,keil还不知道有这个工程,所以我们要在keil新建一个工程,然后把这个工程与存放在磁盘上的目录(还不能叫工程)进行关联,这样才能形成一个完整结构的工程。

\n
\n

打开mdk keil新建stm32工程

这个步骤是为了在keil中新建一个工程与存放在磁盘上的目录(还不能叫工程)进行关联映射,所以有2步:1-新建工程, 2-进行关联

\n

新建工程

注意新建工程时,有几个步骤:

\n
    \n
  1. 工程名,任意(根据自己的命名风格,不能有中文)
  2. \n
  3. 选择芯片,这里使用 STM32F103ZE
  4. \n
  5. 选择库,这里先不选,后面再选,直接跳过
  6. \n
\n
具体步骤 \n
\n

点击 new uVision Project

\"image-20210910210845401\"

自己命名,选择保存;

选择芯片 STM32F103ZE

\"image-20210910211314033\"

选择库,直接跳过,后面再选

\"image-20210910211418672\"

完成

\"image-20210910211508362\"

\n
\n
\n

完善工程结构

这里要把这个工程与磁盘上的目录和文件进行映射关联

\n

选择工程管理,如下:

\n

\"image-20210910212307092\"

\n

依次新建这几个目录:

\n
    \n
  • user: 存放用户代码文件
  • \n
  • startup: 存放启动文件
  • \n
  • CMSIS: 内核库文件
  • \n
  • StdPeriph_Driver: 外设库文件
  • \n
\n

\"image-20210910212703182\"

\n

分别往这几个目录添加文件:

\n

\"image-20210910214210061\"

\n

user(从 /user/目录下选择)

\n
    \n
  • main.c
  • \n
  • stm32f10x_it.c
  • \n
\n

startup(从 /lib/CMSIS/ 目录下选择)

\n
    \n
  • startup_stm32f10x_hd.s(注意添加时没显示出来要选择 文件类型为 “All files(*.*)“)
  • \n
\n

CMSIS(从 /lib/CMSIS/目录下选择)

\n
    \n
  • core_cm3.c
  • \n
  • system_stm32f10x.c
  • \n
\n

StdPeriph_Driver(从/lib/STM32F10x_StdPeriph_Driver/src/目录下选择,可以选择全部,也可以只选择下面两个,根据自己需要,下面这2个一般都会用到,所以都添加进来)

\n
    \n
  • stm32f10x_rcc.c
  • \n
  • stm32f10x_gpio.c
  • \n
\n

添加完成如下:

\n

\"image-20210910214412880\"

\n

到此工程结构已经建立完成,可以在main.c文件下写代码了,但是现在还不能编译,因为编译需要的头文件还没包含进来,以及编译时的其它一些配置还没完成

\n
\n

编译配置

选择这个魔法棒

\n

\"image-20210910215321021\"

\n

配置Target项

\n

\"image-20210910215245013\"

\n

配置output项

\n

\"image-20210910220000653\"

\n

配置 listing项

\n

\"image-20210910220055189\"

\n

配置 C/C++ 项,目的是为了把头文件添加进来

\n

下图的第2步添加的是:USE_STDPERIPH_DRIVER,STM32F10X_HD(注意中间有个英文逗号),表示使用外部库函数驱动

\n

\"image-20210910220625228\"

\n

配置Debug项:ARM仿真器选项配置(手中有开发板可以先通过ARM仿真器连接电脑)

\n

\"image-20210910221520551\"

\n

下载配置(在上一步的Debugger配置中:Debugger->Settings->FlashDownload)

\n

\"image-20210910221912505\"

\n

到此所有配置完成,选择ok,可以愉快的写代码了

\n
\n

测试

打开main.c文件,删除原有代码(如果有的话),添加如下代码:

\n
1
2
3
4
5
6
7
8
9
#include "stm32f10x.h"

int main(){
\t
\twhile(1){

\t}
}

\n

编译运行,没有报错,完美。。。。

\n

\"image-20210910222416127\"

\n"},{"date":"2021-09-11T08:41:28.000Z","title":"stm32第一个程序","description":"使用stm32库函数编写程序点亮led灯","cover":"https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png","abbrlink":"f8dae6c5","_content":"\n\n\n## 新建工程\n\n看这篇[文章](/posts/190b30be.html)\n\n建立完成后工程结构如图:\n\n![image-20210912001909108](https://gitee.com/ajream/images/raw/master/img/20210912001916_image-20210912001909108.png)\n\n## 编写main.c文件\n\n```c\n#include \"stm32f10x.h\"\n\nvoid ledConfig(void);\nvoid ledOn(void);\nvoid ledOff(void);\nvoid delay(unsigned long x);\n\nint main(){\n\t\n\tledConfig();\n\t\n\twhile(1){\n\t\n\t\tledOn();\n\t\tdelay(0x5FFFFF);\n\t\tledOff();\n\t\tdelay(0x5FFFFF);\n\t}\n}\n\n\nvoid ledConfig(void){\n\tGPIO_InitTypeDef GPIO_InitStructure;\n\tRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);\n\tGPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;\n\tGPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;\n\tGPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;\n\tGPIO_Init(GPIOA, &GPIO_InitStructure);\n\n}\n\nvoid ledOn(){\n\tGPIO_ResetBits(GPIOA, GPIO_Pin_8);\n}\n\nvoid ledOff(){\n\n\tGPIO_SetBits(GPIOA, GPIO_Pin_8);\n\n}\n\nvoid delay(unsigned long x){\n\tunsigned long i;\n\tfor(i=0; iStart/Stop Debug Session或工具栏Debug按钮进入调试模式\n\n2. 打开相关窗口添加监测变量或信号\n\n ![image-20210912003102247](https://gitee.com/ajream/images/raw/master/img/20210912003103_image-20210912003102247.png)\n\n 点击setup按钮,在弹出的窗口添加监测变量 `PORTA.8`(表示GPIOA_Pin_8)\n\n ![image-20210912003207021](https://gitee.com/ajream/images/raw/master/img/20210912003208_image-20210912003207021.png)\n\n ![image-20210912003445343](https://gitee.com/ajream/images/raw/master/img/20210912003446_image-20210912003445343.png)\n\n3. 开始运行\n\n ![image-20210912003634281](https://gitee.com/ajream/images/raw/master/img/20210912003635_image-20210912003634281.png)\n\n4. 查看仿真结果\n\n 把代码编辑区下拉后可以看见输出波形图\n\n ![image-20210912003807825](https://gitee.com/ajream/images/raw/master/img/20210912003809_image-20210912003807825.png)\n\n5. 关闭仿真\n\n ![image-20210912003856907](https://gitee.com/ajream/images/raw/master/img/20210912003858_image-20210912003856907.png)\n\n","source":"_posts/stm32/stm32点亮led灯程序.md","raw":"---\ndate: '2021-09-11 16:41:28'\ntitle: stm32第一个程序\ntags:\n - stm32\ncategories:\n - 硬件学习\n - stm32\ndescription: 使用stm32库函数编写程序点亮led灯\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210910172758_stm32.png'\nabbrlink: f8dae6c5\n---\n\n\n\n## 新建工程\n\n看这篇[文章](/posts/190b30be.html)\n\n建立完成后工程结构如图:\n\n![image-20210912001909108](https://gitee.com/ajream/images/raw/master/img/20210912001916_image-20210912001909108.png)\n\n## 编写main.c文件\n\n```c\n#include \"stm32f10x.h\"\n\nvoid ledConfig(void);\nvoid ledOn(void);\nvoid ledOff(void);\nvoid delay(unsigned long x);\n\nint main(){\n\t\n\tledConfig();\n\t\n\twhile(1){\n\t\n\t\tledOn();\n\t\tdelay(0x5FFFFF);\n\t\tledOff();\n\t\tdelay(0x5FFFFF);\n\t}\n}\n\n\nvoid ledConfig(void){\n\tGPIO_InitTypeDef GPIO_InitStructure;\n\tRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);\n\tGPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;\n\tGPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;\n\tGPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;\n\tGPIO_Init(GPIOA, &GPIO_InitStructure);\n\n}\n\nvoid ledOn(){\n\tGPIO_ResetBits(GPIOA, GPIO_Pin_8);\n}\n\nvoid ledOff(){\n\n\tGPIO_SetBits(GPIOA, GPIO_Pin_8);\n\n}\n\nvoid delay(unsigned long x){\n\tunsigned long i;\n\tfor(i=0; iStart/Stop Debug Session或工具栏Debug按钮进入调试模式\n\n2. 打开相关窗口添加监测变量或信号\n\n ![image-20210912003102247](https://gitee.com/ajream/images/raw/master/img/20210912003103_image-20210912003102247.png)\n\n 点击setup按钮,在弹出的窗口添加监测变量 `PORTA.8`(表示GPIOA_Pin_8)\n\n ![image-20210912003207021](https://gitee.com/ajream/images/raw/master/img/20210912003208_image-20210912003207021.png)\n\n ![image-20210912003445343](https://gitee.com/ajream/images/raw/master/img/20210912003446_image-20210912003445343.png)\n\n3. 开始运行\n\n ![image-20210912003634281](https://gitee.com/ajream/images/raw/master/img/20210912003635_image-20210912003634281.png)\n\n4. 查看仿真结果\n\n 把代码编辑区下拉后可以看见输出波形图\n\n ![image-20210912003807825](https://gitee.com/ajream/images/raw/master/img/20210912003809_image-20210912003807825.png)\n\n5. 关闭仿真\n\n ![image-20210912003856907](https://gitee.com/ajream/images/raw/master/img/20210912003858_image-20210912003856907.png)\n\n","slug":"stm32/stm32点亮led灯程序","published":1,"updated":"2021-09-12T01:53:14.761Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6re0086akve1a6d4ffa","content":"

新建工程

看这篇文章

\n

建立完成后工程结构如图:

\n

\"image-20210912001909108\"

\n

编写main.c文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "stm32f10x.h"

void ledConfig(void);
void ledOn(void);
void ledOff(void);
void delay(unsigned long x);

int main(){
\t
\tledConfig();
\t
\twhile(1){
\t
\t\tledOn();
\t\tdelay(0x5FFFFF);
\t\tledOff();
\t\tdelay(0x5FFFFF);
\t}
}


void ledConfig(void){
\tGPIO_InitTypeDef GPIO_InitStructure;
\tRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
\tGPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
\tGPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
\tGPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
\tGPIO_Init(GPIOA, &GPIO_InitStructure);

}

void ledOn(){
\tGPIO_ResetBits(GPIOA, GPIO_Pin_8);
}

void ledOff(){

\tGPIO_SetBits(GPIOA, GPIO_Pin_8);

}

void delay(unsigned long x){
\tunsigned long i;
\tfor(i=0; i<x; ++i);
}


\n

仿真调试

调试前配置

按图中顺序修改配置,其中第4、5步为:

\n
    \n
  • CPU DLL: SARMCM3.DLL, Parameter为空
  • \n
  • Dialog DLL: DARMSTM.DLL, Parameter: -pSTM32F103ZE
  • \n
\n

\"image-20210912002225533\"

\n

进入调试模式

    \n
  1. 选择菜单栏 Debug->Start/Stop Debug Session或工具栏Debug按钮进入调试模式

    \n
  2. \n
  3. 打开相关窗口添加监测变量或信号

    \n

    \"image-20210912003102247\"

    \n

    点击setup按钮,在弹出的窗口添加监测变量 PORTA.8(表示GPIOA_Pin_8)

    \n

    \"image-20210912003207021\"

    \n

    \"image-20210912003445343\"

    \n
  4. \n
  5. 开始运行

    \n

    \"image-20210912003634281\"

    \n
  6. \n
  7. 查看仿真结果

    \n

    把代码编辑区下拉后可以看见输出波形图

    \n

    \"image-20210912003807825\"

    \n
  8. \n
  9. 关闭仿真

    \n

    \"image-20210912003856907\"

    \n
  10. \n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

新建工程

看这篇文章

\n

建立完成后工程结构如图:

\n

\"image-20210912001909108\"

\n

编写main.c文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "stm32f10x.h"

void ledConfig(void);
void ledOn(void);
void ledOff(void);
void delay(unsigned long x);

int main(){
\t
\tledConfig();
\t
\twhile(1){
\t
\t\tledOn();
\t\tdelay(0x5FFFFF);
\t\tledOff();
\t\tdelay(0x5FFFFF);
\t}
}


void ledConfig(void){
\tGPIO_InitTypeDef GPIO_InitStructure;
\tRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
\tGPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
\tGPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
\tGPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
\tGPIO_Init(GPIOA, &GPIO_InitStructure);

}

void ledOn(){
\tGPIO_ResetBits(GPIOA, GPIO_Pin_8);
}

void ledOff(){

\tGPIO_SetBits(GPIOA, GPIO_Pin_8);

}

void delay(unsigned long x){
\tunsigned long i;
\tfor(i=0; i<x; ++i);
}


\n

仿真调试

调试前配置

按图中顺序修改配置,其中第4、5步为:

\n
    \n
  • CPU DLL: SARMCM3.DLL, Parameter为空
  • \n
  • Dialog DLL: DARMSTM.DLL, Parameter: -pSTM32F103ZE
  • \n
\n

\"image-20210912002225533\"

\n

进入调试模式

    \n
  1. 选择菜单栏 Debug->Start/Stop Debug Session或工具栏Debug按钮进入调试模式

    \n
  2. \n
  3. 打开相关窗口添加监测变量或信号

    \n

    \"image-20210912003102247\"

    \n

    点击setup按钮,在弹出的窗口添加监测变量 PORTA.8(表示GPIOA_Pin_8)

    \n

    \"image-20210912003207021\"

    \n

    \"image-20210912003445343\"

    \n
  4. \n
  5. 开始运行

    \n

    \"image-20210912003634281\"

    \n
  6. \n
  7. 查看仿真结果

    \n

    把代码编辑区下拉后可以看见输出波形图

    \n

    \"image-20210912003807825\"

    \n
  8. \n
  9. 关闭仿真

    \n

    \"image-20210912003856907\"

    \n
  10. \n
\n"},{"title":"typora图床配置","description":"使用国内Gitee仓库作为typora图床,提高访问速度","abbrlink":"8b75069d","date":"2021-09-13T12:10:12.000Z","cover":"/img/articles2.png","_content":"\n\n\n\n\n\n\n## 下载相关软件\n\n1. 下载nodejs(自行百度)\n\n2. 安装picgo.exe \n\n 打开typora后,选择 【文件】-【偏好设置】-【图像】,看下图:\n\n \"image-20210419161251100\"\n\n3. 下载插件 gitee-uploader和super-prefix\n\n```sh\n#先切换到picgo安装目录下:cd C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>\nC:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>picgo install gitee-uploader super-prefix\n```\n\n\n\n## gitee配置\n\n1. 在gitee中创建仓库images(必须是public的)以及文件夹img\n\n2. 在gitee中获取私有令牌:\n\n - 在【设置】-【安全设置:私人令牌】-【生成新令牌】\n\n ![在这里插入图片描述](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-35-58_2020101721463059.png)\n\n - 提交之后把 token 复制下来,这个 token 只会出现这一次,丢了就再生成新的令牌\n\n\n\n## picgo插件配置\n\n该配置文件**config.json**在 目录 `C:\\Users\\用户名\\.picgo` 下,刚才下载的插件也在这个目录\n\n![image-20210419154225236](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-42-28_image-20210419154225236.png)\n\n\n\n\n\n打开配置文件config.json,复制下面内容,把原来文件的所有内容覆盖掉\n\n```json\n{\n \"picBed\": {\n \"uploader\": \"gitee\",\n \"current\": \"gitee\",\n \"transformer\": \"path\",\n \"gitee\": {\n \"repo\": \"用户名/images\", // 需要改,仓库名,格式是 user/repo 必填\n \"token\": \"ed1234567890\", // 需要改,gitee 私人令牌 必填(改为自己的)\n \"path\": \"img/\", // 需要改,自定义存储路径,比如 img/ ,但是前提是仓库有这个文件路径,如 user/repo/img\n \"customUrl\": \"\", // 没有自己的域名的话,可以默认为空就行; 如果自定义域名,注意要加http://或者https://,\n \"branch\": \"\" // 分支名,默认是 master\n }\n },\n \"picgoPlugins\": {\n \"picgo-plugin-gitee-uploader\": true,\n \"picgo-plugin-super-prefix\": true\n },\n \"picgo-plugin-super-prefix\": {\n \"prefixFormat\": \"YYYY-MM-DD HH-mm-ss_\" //图片文件名前缀,这么写表示以图片创建时间来命名,注意不能用冒号\n }, //super-prefix插件配置: prefixFormat 或者 fileFormat\n \"picgo-plugin-gitee-uploader\": {\n \"lastSync\": \"\"\n }\n}\n```\n\n需要改的几个地方\n\n1. repo:改为自己的仓库,格式为:\n\n ```json\n \"username/仓库名\"\n \n 如:\n \"xiaojing/images\"\n ```\n\n2. token: gitee的私人令牌,刚刚叫你复制的\n\n3. path:自己仓库下的一个文件夹,例如在仓库images下创建了文件夹img,则为\n\n ```json\n \"img/\"\n ```\n\n\n\n\n\n## typora配置\n\n进入相关设置:【文件】-【偏好设置】-【图像】\n\n![image-20210419155940813](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-59-43_image-20210419155940813.png)\n\n验证成功画面:\n\n\"image-20210419162212020\"\n\n\n\n> 到此配置完成,之后在typora中粘贴图片就会自动上传\n\n","source":"_posts/杂七杂八/gitee图床配置.md","raw":"---\ntitle: typora图床配置\ntags:\n - 图床\ncategories: 杂七杂八\ndescription: 使用国内Gitee仓库作为typora图床,提高访问速度\nabbrlink: 8b75069d\ndate: 2021-09-13 20:10:12\ncover:\n---\n\n\n\n\n\n\n\n## 下载相关软件\n\n1. 下载nodejs(自行百度)\n\n2. 安装picgo.exe \n\n 打开typora后,选择 【文件】-【偏好设置】-【图像】,看下图:\n\n \"image-20210419161251100\"\n\n3. 下载插件 gitee-uploader和super-prefix\n\n```sh\n#先切换到picgo安装目录下:cd C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>\nC:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>picgo install gitee-uploader super-prefix\n```\n\n\n\n## gitee配置\n\n1. 在gitee中创建仓库images(必须是public的)以及文件夹img\n\n2. 在gitee中获取私有令牌:\n\n - 在【设置】-【安全设置:私人令牌】-【生成新令牌】\n\n ![在这里插入图片描述](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-35-58_2020101721463059.png)\n\n - 提交之后把 token 复制下来,这个 token 只会出现这一次,丢了就再生成新的令牌\n\n\n\n## picgo插件配置\n\n该配置文件**config.json**在 目录 `C:\\Users\\用户名\\.picgo` 下,刚才下载的插件也在这个目录\n\n![image-20210419154225236](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-42-28_image-20210419154225236.png)\n\n\n\n\n\n打开配置文件config.json,复制下面内容,把原来文件的所有内容覆盖掉\n\n```json\n{\n \"picBed\": {\n \"uploader\": \"gitee\",\n \"current\": \"gitee\",\n \"transformer\": \"path\",\n \"gitee\": {\n \"repo\": \"用户名/images\", // 需要改,仓库名,格式是 user/repo 必填\n \"token\": \"ed1234567890\", // 需要改,gitee 私人令牌 必填(改为自己的)\n \"path\": \"img/\", // 需要改,自定义存储路径,比如 img/ ,但是前提是仓库有这个文件路径,如 user/repo/img\n \"customUrl\": \"\", // 没有自己的域名的话,可以默认为空就行; 如果自定义域名,注意要加http://或者https://,\n \"branch\": \"\" // 分支名,默认是 master\n }\n },\n \"picgoPlugins\": {\n \"picgo-plugin-gitee-uploader\": true,\n \"picgo-plugin-super-prefix\": true\n },\n \"picgo-plugin-super-prefix\": {\n \"prefixFormat\": \"YYYY-MM-DD HH-mm-ss_\" //图片文件名前缀,这么写表示以图片创建时间来命名,注意不能用冒号\n }, //super-prefix插件配置: prefixFormat 或者 fileFormat\n \"picgo-plugin-gitee-uploader\": {\n \"lastSync\": \"\"\n }\n}\n```\n\n需要改的几个地方\n\n1. repo:改为自己的仓库,格式为:\n\n ```json\n \"username/仓库名\"\n \n 如:\n \"xiaojing/images\"\n ```\n\n2. token: gitee的私人令牌,刚刚叫你复制的\n\n3. path:自己仓库下的一个文件夹,例如在仓库images下创建了文件夹img,则为\n\n ```json\n \"img/\"\n ```\n\n\n\n\n\n## typora配置\n\n进入相关设置:【文件】-【偏好设置】-【图像】\n\n![image-20210419155940813](https://gitee.com/ajream/images/raw/master/img/2021-04-1915-59-43_image-20210419155940813.png)\n\n验证成功画面:\n\n\"image-20210419162212020\"\n\n\n\n> 到此配置完成,之后在typora中粘贴图片就会自动上传\n\n","slug":"杂七杂八/gitee图床配置","published":1,"updated":"2021-09-13T12:30:38.795Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rf008aakve5eeddxx1","content":"

下载相关软件

    \n
  1. 下载nodejs(自行百度)

    \n
  2. \n
  3. 安装picgo.exe

    \n

    打开typora后,选择 【文件】-【偏好设置】-【图像】,看下图:

    \n

    \"image-20210419161251100\"

    \n
  4. \n
  5. 下载插件 gitee-uploader和super-prefix

    \n
  6. \n
\n
1
2
#先切换到picgo安装目录下:cd C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>
C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>picgo install gitee-uploader super-prefix
\n

gitee配置

    \n
  1. 在gitee中创建仓库images(必须是public的)以及文件夹img

    \n
  2. \n
  3. 在gitee中获取私有令牌:

    \n
      \n
    • 在【设置】-【安全设置:私人令牌】-【生成新令牌】
    • \n
    \n

    \"在这里插入图片描述\"

    \n
      \n
    • 提交之后把 token 复制下来,这个 token 只会出现这一次,丢了就再生成新的令牌
    • \n
    \n
  4. \n
\n

picgo插件配置

该配置文件config.json在 目录 C:\\Users\\用户名\\.picgo 下,刚才下载的插件也在这个目录

\n

\"image-20210419154225236\"

\n

打开配置文件config.json,复制下面内容,把原来文件的所有内容覆盖掉

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"picBed": {
"uploader": "gitee",
"current": "gitee",
"transformer": "path",
"gitee": {
"repo": "用户名/images", // 需要改,仓库名,格式是 user/repo 必填
"token": "ed1234567890", // 需要改,gitee 私人令牌 必填(改为自己的)
"path": "img/", // 需要改,自定义存储路径,比如 img/ ,但是前提是仓库有这个文件路径,如 user/repo/img
"customUrl": "", // 没有自己的域名的话,可以默认为空就行; 如果自定义域名,注意要加http://或者https://,
"branch": "" // 分支名,默认是 master
}
},
"picgoPlugins": {
"picgo-plugin-gitee-uploader": true,
"picgo-plugin-super-prefix": true
},
"picgo-plugin-super-prefix": {
"prefixFormat": "YYYY-MM-DD HH-mm-ss_" //图片文件名前缀,这么写表示以图片创建时间来命名,注意不能用冒号
}, //super-prefix插件配置: prefixFormat 或者 fileFormat
"picgo-plugin-gitee-uploader": {
"lastSync": ""
}
}
\n

需要改的几个地方

\n
    \n
  1. repo:改为自己的仓库,格式为:

    \n
    1
    2
    3
    4
    "username/仓库名"

    如:
    "xiaojing/images"
    \n
  2. \n
  3. token: gitee的私人令牌,刚刚叫你复制的

    \n
  4. \n
  5. path:自己仓库下的一个文件夹,例如在仓库images下创建了文件夹img,则为

    \n
    1
    "img/"
    \n
  6. \n
\n

typora配置

进入相关设置:【文件】-【偏好设置】-【图像】

\n

\"image-20210419155940813\"

\n

验证成功画面:

\n

\"image-20210419162212020\"

\n
\n

到此配置完成,之后在typora中粘贴图片就会自动上传

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

下载相关软件

    \n
  1. 下载nodejs(自行百度)

    \n
  2. \n
  3. 安装picgo.exe

    \n

    打开typora后,选择 【文件】-【偏好设置】-【图像】,看下图:

    \n

    \"image-20210419161251100\"

    \n
  4. \n
  5. 下载插件 gitee-uploader和super-prefix

    \n
  6. \n
\n
1
2
#先切换到picgo安装目录下:cd C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>
C:\\Users\\用户名\\AppData\\Roaming\\Typora\\picgo\\win64>picgo install gitee-uploader super-prefix
\n

gitee配置

    \n
  1. 在gitee中创建仓库images(必须是public的)以及文件夹img

    \n
  2. \n
  3. 在gitee中获取私有令牌:

    \n
      \n
    • 在【设置】-【安全设置:私人令牌】-【生成新令牌】
    • \n
    \n

    \"在这里插入图片描述\"

    \n
      \n
    • 提交之后把 token 复制下来,这个 token 只会出现这一次,丢了就再生成新的令牌
    • \n
    \n
  4. \n
\n

picgo插件配置

该配置文件config.json在 目录 C:\\Users\\用户名\\.picgo 下,刚才下载的插件也在这个目录

\n

\"image-20210419154225236\"

\n

打开配置文件config.json,复制下面内容,把原来文件的所有内容覆盖掉

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"picBed": {
"uploader": "gitee",
"current": "gitee",
"transformer": "path",
"gitee": {
"repo": "用户名/images", // 需要改,仓库名,格式是 user/repo 必填
"token": "ed1234567890", // 需要改,gitee 私人令牌 必填(改为自己的)
"path": "img/", // 需要改,自定义存储路径,比如 img/ ,但是前提是仓库有这个文件路径,如 user/repo/img
"customUrl": "", // 没有自己的域名的话,可以默认为空就行; 如果自定义域名,注意要加http://或者https://,
"branch": "" // 分支名,默认是 master
}
},
"picgoPlugins": {
"picgo-plugin-gitee-uploader": true,
"picgo-plugin-super-prefix": true
},
"picgo-plugin-super-prefix": {
"prefixFormat": "YYYY-MM-DD HH-mm-ss_" //图片文件名前缀,这么写表示以图片创建时间来命名,注意不能用冒号
}, //super-prefix插件配置: prefixFormat 或者 fileFormat
"picgo-plugin-gitee-uploader": {
"lastSync": ""
}
}
\n

需要改的几个地方

\n
    \n
  1. repo:改为自己的仓库,格式为:

    \n
    1
    2
    3
    4
    "username/仓库名"

    如:
    "xiaojing/images"
    \n
  2. \n
  3. token: gitee的私人令牌,刚刚叫你复制的

    \n
  4. \n
  5. path:自己仓库下的一个文件夹,例如在仓库images下创建了文件夹img,则为

    \n
    1
    "img/"
    \n
  6. \n
\n

typora配置

进入相关设置:【文件】-【偏好设置】-【图像】

\n

\"image-20210419155940813\"

\n

验证成功画面:

\n

\"image-20210419162212020\"

\n
\n

到此配置完成,之后在typora中粘贴图片就会自动上传

\n
\n"},{"title":"矢量场","notes":"电磁场","katex":true,"description":"矢量运算","abbrlink":"401491fc","date":"2021-09-08T08:00:00.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210902230207_dianci.png","_content":"\n\n### 矢量运算\n\n矢量加法、减法、数乘跳过\n\n#### 矢量的标量积(点积)\n$$\\vec{A} \\cdot \\vec{B} = |A||B|cos\\theta$$\n\n特点:相互垂直的两个矢量的标量积为0\n\n矢量点积计算\n\n$$\n\\vec{A} \\cdot \\vec{B} = A_xB_x\\vec{a_x} + A_yB_y\\vec{a_y} + A_zB_z\\vec{a_z}\n$$\n\n#### 矢量的矢量积(叉积)\n$$\\vec{C}=\\vec{A} \\times \\vec{B} = |A||B|sin\\theta ~ \\vec{a}~~~~~~~~~~~~~~~~~~0\\leq\\theta\\leq\\pi$$ \n特点:相互平行的两个矢量的矢量积为0\n\n\n\n\n矢量的叉积计算\n$$\n\\vec{A} \\times \\vec{B} =\n\\begin{vmatrix} \n\\vec{a_x} & \\vec{a_y} & \\vec{a_z} \\\\\nA_x & A_y & A_z \\\\\nB_x&B_y&B_z \\\\\n\\end{vmatrix}\n$$\n\n\n\n### 矢量微元\n\n直角坐标系\n\n","source":"_posts/电磁场/矢量场.md","raw":"---\ntitle: 矢量场\ntags:\n - 矢量\ncategories:\n - 电磁场\n - 矢量\nnotes: 电磁场\nkatex: true\ndescription: 矢量运算\nabbrlink: 401491fc\ndate: 2021-09-08 16:00:00\ncover: https://gitee.com/ajream/images/raw/master/img/20210902230207_dianci.png\n---\n\n\n### 矢量运算\n\n矢量加法、减法、数乘跳过\n\n#### 矢量的标量积(点积)\n$$\\vec{A} \\cdot \\vec{B} = |A||B|cos\\theta$$\n\n特点:相互垂直的两个矢量的标量积为0\n\n矢量点积计算\n\n$$\n\\vec{A} \\cdot \\vec{B} = A_xB_x\\vec{a_x} + A_yB_y\\vec{a_y} + A_zB_z\\vec{a_z}\n$$\n\n#### 矢量的矢量积(叉积)\n$$\\vec{C}=\\vec{A} \\times \\vec{B} = |A||B|sin\\theta ~ \\vec{a}~~~~~~~~~~~~~~~~~~0\\leq\\theta\\leq\\pi$$ \n特点:相互平行的两个矢量的矢量积为0\n\n\n\n\n矢量的叉积计算\n$$\n\\vec{A} \\times \\vec{B} =\n\\begin{vmatrix} \n\\vec{a_x} & \\vec{a_y} & \\vec{a_z} \\\\\nA_x & A_y & A_z \\\\\nB_x&B_y&B_z \\\\\n\\end{vmatrix}\n$$\n\n\n\n### 矢量微元\n\n直角坐标系\n\n","slug":"电磁场/矢量场","published":1,"updated":"2021-09-08T09:14:19.444Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rg008cakve0mvn1u4x","content":"

矢量运算

矢量加法、减法、数乘跳过

\n

矢量的标量积(点积)

特点:相互垂直的两个矢量的标量积为0

\n

矢量点积计算

\n

矢量的矢量积(叉积)

特点:相互平行的两个矢量的矢量积为0

\n

矢量的叉积计算

\n

矢量微元

直角坐标系

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

矢量运算

矢量加法、减法、数乘跳过

\n

矢量的标量积(点积)

特点:相互垂直的两个矢量的标量积为0

\n

矢量点积计算

\n

矢量的矢量积(叉积)

特点:相互平行的两个矢量的矢量积为0

\n

矢量的叉积计算

\n

矢量微元

直角坐标系

\n"},{"title":"网络协议","description":"计算机网络协议protocol","cover":"https://gitee.com/ajream/images/raw/master/img/20210913084452_c-s.png","abbrlink":"1cd3002f","date":"2021-09-12T01:26:15.000Z","_content":"\n\n\n\n\n## 组成要素\n\n1. 语义: 语义是解释控制信息每个部分的意义。它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应\n\n2. 语法: 语法是用户数据与控制信息的结构与格式,以及数据出现的顺序\n\n3. 时序: 时序是对事件发生顺序的详细说明。(也可称为“同步”)\n\n\n\n{% note default modern %}\n人们形象地把这三个要素描述为:语义表示要做什么,语法表示要怎么做,时序表示做的顺序。\n{% endnote %}\n\n\n\n\n\n\n\n## OSI参考模型\n\n\n\n**OSI七层模型**(Open System Interconnect)即开放系统互连参考模型,是由**ISO**(International Organization for Standardization)**国际标准化组织**提出的,用于计算机或通信系统间互联的标准体系。\n\n\n\nOSI模型从上到下可分为七层,每一层都完成特定的功能,并为上一层提供服务,并使用下层所提供的服务。\n\n应用层:协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP\n\n表示层:数据的表示、安全、压缩。格式有,JPEG、ASCll、DECOIC、加密格式等\n\n会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话\n\n传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层\n\n网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP\n\n数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。\n\n物理层:建立、维护、断开物理连接。\n\n\n\n![OSImodel](https://gitee.com/ajream/images/raw/master/img/20210913093639_OSImodel.png)\n\n\n\n数据传输过程\n\n\n\n![img](https://upload-images.jianshu.io/upload_images/7541336-906a34d0af992f70.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp)\n\n\n\n## TCP-IP模型\n\n\n\nTCP/IP 模型是由 OSI 模型演化而来,TCP/IP 模型将 OSI 模型由七层简化为五层(一开始为四层),应用层、表示层、会话层统一为应用层。\n\n\n\n![image-20210913094813894](https://gitee.com/ajream/images/raw/master/img/20210913094816_image-20210913094813894.png)\n\n\n\n### 协议\n\nTCP/IP协议被称为传输控制协议/互联网协议,又称网络通讯协议(Transmission Control Protocol)。是由网络层的IP协议和传输层的TCP协议组成,是一个很大的协议集合。\n\n- 物理层和数据链路层没有定义任何特定协议,支持所有的标准和专用的协议。\n- 网络层定义了网络互联也就是IP协议,主要包括IP、ARP、RARP、ICMP、IGMP。\n- 传输层定义了TCP和UDP(User Datagram Protocol),我们会后面重点介绍一下TCP协议。\n- 应用层定义了HTTP(超文本传输协议)、FTP(文件传输协议)、DNS(域名系统)等协议。\n\n\n\n### 物理层\n\n计算机在传递数据的时候传递的都是0和1的数字,而物理层关心的是用什么信号来表示0和1,是否可以双向通信,最初的连接如何建立以及完成连接如何终止,总之,物理层是为数据传输提供可靠的环境。\n\n### 数据链路层\n\n数据链路层们于物理层和网络层之间,用来向网络层提供数据,就是把源计算机网络层传过来的信息传递给目标主机。\n数据链路层主要的作用包括:\n\n- 如何将数据组合成数据帧(Frame),帧是数据链路层的传输单位\n- 数据链路的建立、维护和拆除\n- 帧包装、帧传输、帧同步\n- 帧的差错恢复\n- 流量控制\n\n### 网络层\n\n网络层位于传输层和数据链路层之间,用于把数据从源主机经过若干个中间节点传送到目标主机,并向传输层提供最基础的数据传输服务,它要提供路由和选址的工作。\n\n### 传输层\n\n传输层主要提供以下几个功能\n\n1. 提供应用程序接口,为网络应用程序提供网络访问的途径;\n\n2. 提供可以从多个应用层序接收消息的功能(多路复用),同时也提供可以把消息分发给应用程序的功能(多路分解)。\n\n3. 对数据进行错误检测、流量控制。\n\n\n\n### 应用层\n\nTCP/IP的应用层对应于OSI的应用层、会话层、网络层,它们分别功能如下\n\n应用层:为用户的应用提供服务并支持网络访问。\n\n会话层:负责管理网络中计算之间的通信,提供传输层不具备的连接相关功能。\n\n表示层:负责转化数据格式,并处理数据加密和数据压缩。\n\n\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20201218111758350.png)","source":"_posts/网络通信/网络协议.md","raw":"---\ntitle: 网络协议\ntags:\n - 网络通信\ncategories:\n - 网络通信\ndescription: 计算机网络协议protocol\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210913084452_c-s.png'\nabbrlink: 1cd3002f\ndate: 2021-09-12 09:26:15\n---\n\n\n\n\n\n## 组成要素\n\n1. 语义: 语义是解释控制信息每个部分的意义。它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应\n\n2. 语法: 语法是用户数据与控制信息的结构与格式,以及数据出现的顺序\n\n3. 时序: 时序是对事件发生顺序的详细说明。(也可称为“同步”)\n\n\n\n{% note default modern %}\n人们形象地把这三个要素描述为:语义表示要做什么,语法表示要怎么做,时序表示做的顺序。\n{% endnote %}\n\n\n\n\n\n\n\n## OSI参考模型\n\n\n\n**OSI七层模型**(Open System Interconnect)即开放系统互连参考模型,是由**ISO**(International Organization for Standardization)**国际标准化组织**提出的,用于计算机或通信系统间互联的标准体系。\n\n\n\nOSI模型从上到下可分为七层,每一层都完成特定的功能,并为上一层提供服务,并使用下层所提供的服务。\n\n应用层:协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP\n\n表示层:数据的表示、安全、压缩。格式有,JPEG、ASCll、DECOIC、加密格式等\n\n会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话\n\n传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层\n\n网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP\n\n数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。\n\n物理层:建立、维护、断开物理连接。\n\n\n\n![OSImodel](https://gitee.com/ajream/images/raw/master/img/20210913093639_OSImodel.png)\n\n\n\n数据传输过程\n\n\n\n![img](https://upload-images.jianshu.io/upload_images/7541336-906a34d0af992f70.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp)\n\n\n\n## TCP-IP模型\n\n\n\nTCP/IP 模型是由 OSI 模型演化而来,TCP/IP 模型将 OSI 模型由七层简化为五层(一开始为四层),应用层、表示层、会话层统一为应用层。\n\n\n\n![image-20210913094813894](https://gitee.com/ajream/images/raw/master/img/20210913094816_image-20210913094813894.png)\n\n\n\n### 协议\n\nTCP/IP协议被称为传输控制协议/互联网协议,又称网络通讯协议(Transmission Control Protocol)。是由网络层的IP协议和传输层的TCP协议组成,是一个很大的协议集合。\n\n- 物理层和数据链路层没有定义任何特定协议,支持所有的标准和专用的协议。\n- 网络层定义了网络互联也就是IP协议,主要包括IP、ARP、RARP、ICMP、IGMP。\n- 传输层定义了TCP和UDP(User Datagram Protocol),我们会后面重点介绍一下TCP协议。\n- 应用层定义了HTTP(超文本传输协议)、FTP(文件传输协议)、DNS(域名系统)等协议。\n\n\n\n### 物理层\n\n计算机在传递数据的时候传递的都是0和1的数字,而物理层关心的是用什么信号来表示0和1,是否可以双向通信,最初的连接如何建立以及完成连接如何终止,总之,物理层是为数据传输提供可靠的环境。\n\n### 数据链路层\n\n数据链路层们于物理层和网络层之间,用来向网络层提供数据,就是把源计算机网络层传过来的信息传递给目标主机。\n数据链路层主要的作用包括:\n\n- 如何将数据组合成数据帧(Frame),帧是数据链路层的传输单位\n- 数据链路的建立、维护和拆除\n- 帧包装、帧传输、帧同步\n- 帧的差错恢复\n- 流量控制\n\n### 网络层\n\n网络层位于传输层和数据链路层之间,用于把数据从源主机经过若干个中间节点传送到目标主机,并向传输层提供最基础的数据传输服务,它要提供路由和选址的工作。\n\n### 传输层\n\n传输层主要提供以下几个功能\n\n1. 提供应用程序接口,为网络应用程序提供网络访问的途径;\n\n2. 提供可以从多个应用层序接收消息的功能(多路复用),同时也提供可以把消息分发给应用程序的功能(多路分解)。\n\n3. 对数据进行错误检测、流量控制。\n\n\n\n### 应用层\n\nTCP/IP的应用层对应于OSI的应用层、会话层、网络层,它们分别功能如下\n\n应用层:为用户的应用提供服务并支持网络访问。\n\n会话层:负责管理网络中计算之间的通信,提供传输层不具备的连接相关功能。\n\n表示层:负责转化数据格式,并处理数据加密和数据压缩。\n\n\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20201218111758350.png)","slug":"网络通信/网络协议","published":1,"updated":"2021-09-13T01:54:14.362Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6rh008hakveh2yhheff","content":"

组成要素

    \n
  1. 语义: 语义是解释控制信息每个部分的意义。它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应

    \n
  2. \n
  3. 语法: 语法是用户数据与控制信息的结构与格式,以及数据出现的顺序

    \n
  4. \n
  5. 时序: 时序是对事件发生顺序的详细说明。(也可称为“同步”)

    \n
  6. \n
\n

人们形象地把这三个要素描述为:语义表示要做什么,语法表示要怎么做,时序表示做的顺序。

\n
\n

OSI参考模型

OSI七层模型(Open System Interconnect)即开放系统互连参考模型,是由ISO(International Organization for Standardization)国际标准化组织提出的,用于计算机或通信系统间互联的标准体系。

\n

OSI模型从上到下可分为七层,每一层都完成特定的功能,并为上一层提供服务,并使用下层所提供的服务。

\n

应用层:协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP

\n

表示层:数据的表示、安全、压缩。格式有,JPEG、ASCll、DECOIC、加密格式等

\n

会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话

\n

传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层

\n

网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP

\n

数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。

\n

物理层:建立、维护、断开物理连接。

\n

\"OSImodel\"

\n

数据传输过程

\n

\"img\"

\n

TCP-IP模型

TCP/IP 模型是由 OSI 模型演化而来,TCP/IP 模型将 OSI 模型由七层简化为五层(一开始为四层),应用层、表示层、会话层统一为应用层。

\n

\"image-20210913094813894\"

\n

协议

TCP/IP协议被称为传输控制协议/互联网协议,又称网络通讯协议(Transmission Control Protocol)。是由网络层的IP协议和传输层的TCP协议组成,是一个很大的协议集合。

\n
    \n
  • 物理层和数据链路层没有定义任何特定协议,支持所有的标准和专用的协议。
  • \n
  • 网络层定义了网络互联也就是IP协议,主要包括IP、ARP、RARP、ICMP、IGMP。
  • \n
  • 传输层定义了TCP和UDP(User Datagram Protocol),我们会后面重点介绍一下TCP协议。
  • \n
  • 应用层定义了HTTP(超文本传输协议)、FTP(文件传输协议)、DNS(域名系统)等协议。
  • \n
\n

物理层

计算机在传递数据的时候传递的都是0和1的数字,而物理层关心的是用什么信号来表示0和1,是否可以双向通信,最初的连接如何建立以及完成连接如何终止,总之,物理层是为数据传输提供可靠的环境。

\n

数据链路层

数据链路层们于物理层和网络层之间,用来向网络层提供数据,就是把源计算机网络层传过来的信息传递给目标主机。
数据链路层主要的作用包括:

\n
    \n
  • 如何将数据组合成数据帧(Frame),帧是数据链路层的传输单位
  • \n
  • 数据链路的建立、维护和拆除
  • \n
  • 帧包装、帧传输、帧同步
  • \n
  • 帧的差错恢复
  • \n
  • 流量控制
  • \n
\n

网络层

网络层位于传输层和数据链路层之间,用于把数据从源主机经过若干个中间节点传送到目标主机,并向传输层提供最基础的数据传输服务,它要提供路由和选址的工作。

\n

传输层

传输层主要提供以下几个功能

\n
    \n
  1. 提供应用程序接口,为网络应用程序提供网络访问的途径;

    \n
  2. \n
  3. 提供可以从多个应用层序接收消息的功能(多路复用),同时也提供可以把消息分发给应用程序的功能(多路分解)。

    \n
  4. \n
  5. 对数据进行错误检测、流量控制。

    \n
  6. \n
\n

应用层

TCP/IP的应用层对应于OSI的应用层、会话层、网络层,它们分别功能如下

\n

应用层:为用户的应用提供服务并支持网络访问。

\n

会话层:负责管理网络中计算之间的通信,提供传输层不具备的连接相关功能。

\n

表示层:负责转化数据格式,并处理数据加密和数据压缩。

\n

\"在这里插入图片描述\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

组成要素

    \n
  1. 语义: 语义是解释控制信息每个部分的意义。它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应

    \n
  2. \n
  3. 语法: 语法是用户数据与控制信息的结构与格式,以及数据出现的顺序

    \n
  4. \n
  5. 时序: 时序是对事件发生顺序的详细说明。(也可称为“同步”)

    \n
  6. \n
\n

人们形象地把这三个要素描述为:语义表示要做什么,语法表示要怎么做,时序表示做的顺序。

\n
\n

OSI参考模型

OSI七层模型(Open System Interconnect)即开放系统互连参考模型,是由ISO(International Organization for Standardization)国际标准化组织提出的,用于计算机或通信系统间互联的标准体系。

\n

OSI模型从上到下可分为七层,每一层都完成特定的功能,并为上一层提供服务,并使用下层所提供的服务。

\n

应用层:协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP

\n

表示层:数据的表示、安全、压缩。格式有,JPEG、ASCll、DECOIC、加密格式等

\n

会话层:建立、管理、终止会话。对应主机进程,指本地主机与远程主机正在进行的会话

\n

传输层:定义传输数据的协议端口号,以及流控和差错校验。协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层

\n

网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。协议有:ICMP IGMP IP(IPV4 IPV6) ARP RARP

\n

数据链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。

\n

物理层:建立、维护、断开物理连接。

\n

\"OSImodel\"

\n

数据传输过程

\n

\"img\"

\n

TCP-IP模型

TCP/IP 模型是由 OSI 模型演化而来,TCP/IP 模型将 OSI 模型由七层简化为五层(一开始为四层),应用层、表示层、会话层统一为应用层。

\n

\"image-20210913094813894\"

\n

协议

TCP/IP协议被称为传输控制协议/互联网协议,又称网络通讯协议(Transmission Control Protocol)。是由网络层的IP协议和传输层的TCP协议组成,是一个很大的协议集合。

\n
    \n
  • 物理层和数据链路层没有定义任何特定协议,支持所有的标准和专用的协议。
  • \n
  • 网络层定义了网络互联也就是IP协议,主要包括IP、ARP、RARP、ICMP、IGMP。
  • \n
  • 传输层定义了TCP和UDP(User Datagram Protocol),我们会后面重点介绍一下TCP协议。
  • \n
  • 应用层定义了HTTP(超文本传输协议)、FTP(文件传输协议)、DNS(域名系统)等协议。
  • \n
\n

物理层

计算机在传递数据的时候传递的都是0和1的数字,而物理层关心的是用什么信号来表示0和1,是否可以双向通信,最初的连接如何建立以及完成连接如何终止,总之,物理层是为数据传输提供可靠的环境。

\n

数据链路层

数据链路层们于物理层和网络层之间,用来向网络层提供数据,就是把源计算机网络层传过来的信息传递给目标主机。
数据链路层主要的作用包括:

\n
    \n
  • 如何将数据组合成数据帧(Frame),帧是数据链路层的传输单位
  • \n
  • 数据链路的建立、维护和拆除
  • \n
  • 帧包装、帧传输、帧同步
  • \n
  • 帧的差错恢复
  • \n
  • 流量控制
  • \n
\n

网络层

网络层位于传输层和数据链路层之间,用于把数据从源主机经过若干个中间节点传送到目标主机,并向传输层提供最基础的数据传输服务,它要提供路由和选址的工作。

\n

传输层

传输层主要提供以下几个功能

\n
    \n
  1. 提供应用程序接口,为网络应用程序提供网络访问的途径;

    \n
  2. \n
  3. 提供可以从多个应用层序接收消息的功能(多路复用),同时也提供可以把消息分发给应用程序的功能(多路分解)。

    \n
  4. \n
  5. 对数据进行错误检测、流量控制。

    \n
  6. \n
\n

应用层

TCP/IP的应用层对应于OSI的应用层、会话层、网络层,它们分别功能如下

\n

应用层:为用户的应用提供服务并支持网络访问。

\n

会话层:负责管理网络中计算之间的通信,提供传输层不具备的连接相关功能。

\n

表示层:负责转化数据格式,并处理数据加密和数据压缩。

\n

\"在这里插入图片描述\"

\n"},{"title":"计算机网络的产生","description":"计算机网络的产生与发展过程、计算机网络组成与结构、计算机网络性能指标","cover":"https://gitee.com/ajream/images/raw/master/img/20210913084452_c-s.png","abbrlink":"7dc1e032","date":"2021-09-12T00:25:15.000Z","katex":true,"_content":"\n\n## 计算机网络的产生\n\n### 分组交换网的出现\n\n分组交换技术是在**1960年代末**出现的,当时美国高级研究计划局(简称ARPA)为实现远程计算机之间的信息交换,资助建设一个试验性的网络,该网络被称为ARPANET。 ARPANET的主要研究成果之一就是开发一种新的网络协议 ,在 ARPANET 网络上对话必须使用这种网络协议。\n\n### 局域网、城域网、广域网\n\n#### 局域网\n\n1、局域网定义\n\n局域网(Local Area Network),简称LAN,是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。\n\n2、局域网介绍\n\n局域网(Local Area Network,LAN)是在一个局部的地理范围内(如一个学校、工厂和机关内),一般是方圆几千米以内,将各种计算机,外部设备和数据库等互相联接起来组成的计算机通信网。它可以通过数据通信网或专用数据电路,与远方的局域网、数据库或处理中心相连接,构成一个较大范围的信息处理系统。局域网可以实现文件管理、应用软件共享、打印机共享、扫描仪共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网严格意义上是封闭型的。它可以由办公室内几台甚至上千上万台计算机组成。决定局域网的主要技术要素为:网络拓扑,传输介质与介质访问控制方法。\n\n3、局域网组成\n\n局域网由硬件(包括服务器、工作站、打印机、网卡、互联设备等)和网络传输介质(光纤和WIFI等),以及软件所组成。\n\n4、局域网拓扑结构\n\n星形、树形、总线形和环形。\n\n5、局域网四种技术类型/标准\n\n以太网(Ethernet);令牌环(Token Ring);令牌总线(Token Bus),如ARCNET;光纤分布式数据接口(FDDI)。目前,以太网(Ethernet)类型的局域网应用最为广泛。\n\n#### 城域网\n\n1、城域网定义\n\n城域网(Metropolitan Area Network),简称MAN,是在一个城市范围内所建立的计算机通信网。属宽带局域网。由于采用具有有源交换元件的局域网技术,网中传输时延较小,它的传输媒介主要采用光缆,传输速率在100兆比特/秒以上。\n\n2、城域网介绍\n\n城域网MAN是基于一种大型的局域网LAN,通常使用与局域网LAN相似的技术。将城域网单独列出来的一个主要原因是它有自己的一个标准:分布式队列双总线DQDB(Distributed Queue Dual Bus),即IEEE802.6。DQDB是由双总线构成,所有的计算机都连结在上面。\n\nMAN的一个重要用途是用作骨干网,通过它将位于同一城市内不同地点的主机、数据库,以及LAN等互相联接起来,这与WAN的作用有相似之处,但两者在实现方法与性能上有很大差别。\n\n建设局域网或广域网包括资源子网和通信子网两个方面,而城域网的建设主要集中在通信子网上,其中也包含两个方面:\n\n- 一是城市骨干网,它与中国的骨干网相连。\n\n- 二是城市接入网,它把本地所有的联网用户与城市骨干网相连。\n\n#### 广域网\n\n广域网(Wide Area Network),简称 WAN,又称广域网、外网、公网(相对应,局域网LAN也称内网、私网)。是连接不同地区[局域网](https://baike.baidu.com/item/局域网)或[城域网](https://baike.baidu.com/item/城域网)计算机通信的远程网。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。广域网并不等同于[互联网](https://baike.baidu.com/item/互联网)。\n\n广域网是网络专业的一个专业术语,通常特指跨接很大物理范围的[计算机网络](https://www.baidu.com/s?wd=计算机网络&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao),一般要超过几十公里,比如某公司总部在北京,分公司在上海,这两个地方的分别有各自的局域网,这两个局域网通过自建或者租用通讯线路的方式连接起来就构成了一个广域网络。互联网也可以说是广域网的一个实例\n\n\n\n### 计算机体系结构形成\n\n第一阶段:从单个网络(分组交换网ARPANET)->互联网\n\n第二阶段:三级结构因特网的形成【国家主干网->地区网->校园网】\n\n第三阶段:多级结构因特网\n\n\n{% note modern %}\n\nISP: 因特网服务供应商\n\nNAP: 网络接入点\n\n{% endnote %}\n\n\n\n\n\n\n\n## 计算机网络组成与结构\n\n\n\n结构与功能密不可分,计算机网络具有两大功能:数据处理与数据通信,因此计算机组成结构可以据此分为资源子网、通信子网\n\n资源子网:主计算机 + 终端\n\n通信子网:通信控制处理机 + 通信线路\n\n\n\n## 计算机网络性能指标\n\n\n\n### 带宽\n\n带宽一词最初指的是电磁波频带的宽度,也就是信号的最高频率与最低频率的差值。目前,它被更广泛地借用在数字通信中,用来描述网络或线路理论上传输数据的最高速率。\n\n常用带宽单位:bit/s(bps)、kb/s(kbps)、Mb/s(=$10^6$ b/s)、 Gb/s(=$10^9$ b/s)、Tb/s(=$10^{12}$ b/s)……生活中为了方便常常省略后面的“b/s”,比如“线路带宽是10M”,实际指的是 10Mb/s.\n\n\n\n### 时延\n\n\n\n时延是指一个报文或分组从一个网络的一端传送到另一个端所需要的时间。它包括了发送时延,传播时延,处理时延。(时延=发送时延+传播时延+处理时延)一般,发送时延与传播时延是我们主要考虑的。对于报文长度较大的情况,发送时延是主要矛盾;报文长度较小的情况,传播时延是主要矛盾。\n\n\n\n发送时延:指结点在发送数据时使数据块从结点进入到传输媒体所需的时间,也就是从数据块的第一个bit开始发送算起,到最后一个bit发送完毕所需的时间。\n\n发送时延=数据帧长度/发送速率\n\n\n\n传播时延:指电磁信号或光信号在传输介质中传播一定的距离所花费的时间,即从发送端发送数据开始,到接收端收到数据(或者从接收端发送确认帧,到发送端收到确认帧),总共经历的时间。\n\n\n\n处理时延:指数据在交换结点为了存储转发而进行一些必要的处理所花费的时间,在结点缓存队列中,分组排队所经历的时延是处理时延的重要组成部分。\n\n\n\n\n\n\n\n","source":"_posts/网络通信/计算机网络的产生.md","raw":"---\ntitle: 计算机网络的产生\ntags:\n - 网络通信\ncategories:\n - 网络通信\ndescription: 计算机网络的产生与发展过程、计算机网络组成与结构、计算机网络性能指标\ncover: 'https://gitee.com/ajream/images/raw/master/img/20210913084452_c-s.png'\nabbrlink: 7dc1e032\ndate: 2021-09-12 08:25:15\nkatex: true\n---\n\n\n## 计算机网络的产生\n\n### 分组交换网的出现\n\n分组交换技术是在**1960年代末**出现的,当时美国高级研究计划局(简称ARPA)为实现远程计算机之间的信息交换,资助建设一个试验性的网络,该网络被称为ARPANET。 ARPANET的主要研究成果之一就是开发一种新的网络协议 ,在 ARPANET 网络上对话必须使用这种网络协议。\n\n### 局域网、城域网、广域网\n\n#### 局域网\n\n1、局域网定义\n\n局域网(Local Area Network),简称LAN,是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。\n\n2、局域网介绍\n\n局域网(Local Area Network,LAN)是在一个局部的地理范围内(如一个学校、工厂和机关内),一般是方圆几千米以内,将各种计算机,外部设备和数据库等互相联接起来组成的计算机通信网。它可以通过数据通信网或专用数据电路,与远方的局域网、数据库或处理中心相连接,构成一个较大范围的信息处理系统。局域网可以实现文件管理、应用软件共享、打印机共享、扫描仪共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网严格意义上是封闭型的。它可以由办公室内几台甚至上千上万台计算机组成。决定局域网的主要技术要素为:网络拓扑,传输介质与介质访问控制方法。\n\n3、局域网组成\n\n局域网由硬件(包括服务器、工作站、打印机、网卡、互联设备等)和网络传输介质(光纤和WIFI等),以及软件所组成。\n\n4、局域网拓扑结构\n\n星形、树形、总线形和环形。\n\n5、局域网四种技术类型/标准\n\n以太网(Ethernet);令牌环(Token Ring);令牌总线(Token Bus),如ARCNET;光纤分布式数据接口(FDDI)。目前,以太网(Ethernet)类型的局域网应用最为广泛。\n\n#### 城域网\n\n1、城域网定义\n\n城域网(Metropolitan Area Network),简称MAN,是在一个城市范围内所建立的计算机通信网。属宽带局域网。由于采用具有有源交换元件的局域网技术,网中传输时延较小,它的传输媒介主要采用光缆,传输速率在100兆比特/秒以上。\n\n2、城域网介绍\n\n城域网MAN是基于一种大型的局域网LAN,通常使用与局域网LAN相似的技术。将城域网单独列出来的一个主要原因是它有自己的一个标准:分布式队列双总线DQDB(Distributed Queue Dual Bus),即IEEE802.6。DQDB是由双总线构成,所有的计算机都连结在上面。\n\nMAN的一个重要用途是用作骨干网,通过它将位于同一城市内不同地点的主机、数据库,以及LAN等互相联接起来,这与WAN的作用有相似之处,但两者在实现方法与性能上有很大差别。\n\n建设局域网或广域网包括资源子网和通信子网两个方面,而城域网的建设主要集中在通信子网上,其中也包含两个方面:\n\n- 一是城市骨干网,它与中国的骨干网相连。\n\n- 二是城市接入网,它把本地所有的联网用户与城市骨干网相连。\n\n#### 广域网\n\n广域网(Wide Area Network),简称 WAN,又称广域网、外网、公网(相对应,局域网LAN也称内网、私网)。是连接不同地区[局域网](https://baike.baidu.com/item/局域网)或[城域网](https://baike.baidu.com/item/城域网)计算机通信的远程网。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。广域网并不等同于[互联网](https://baike.baidu.com/item/互联网)。\n\n广域网是网络专业的一个专业术语,通常特指跨接很大物理范围的[计算机网络](https://www.baidu.com/s?wd=计算机网络&tn=SE_PcZhidaonwhc_ngpagmjz&rsv_dl=gh_pc_zhidao),一般要超过几十公里,比如某公司总部在北京,分公司在上海,这两个地方的分别有各自的局域网,这两个局域网通过自建或者租用通讯线路的方式连接起来就构成了一个广域网络。互联网也可以说是广域网的一个实例\n\n\n\n### 计算机体系结构形成\n\n第一阶段:从单个网络(分组交换网ARPANET)->互联网\n\n第二阶段:三级结构因特网的形成【国家主干网->地区网->校园网】\n\n第三阶段:多级结构因特网\n\n\n{% note modern %}\n\nISP: 因特网服务供应商\n\nNAP: 网络接入点\n\n{% endnote %}\n\n\n\n\n\n\n\n## 计算机网络组成与结构\n\n\n\n结构与功能密不可分,计算机网络具有两大功能:数据处理与数据通信,因此计算机组成结构可以据此分为资源子网、通信子网\n\n资源子网:主计算机 + 终端\n\n通信子网:通信控制处理机 + 通信线路\n\n\n\n## 计算机网络性能指标\n\n\n\n### 带宽\n\n带宽一词最初指的是电磁波频带的宽度,也就是信号的最高频率与最低频率的差值。目前,它被更广泛地借用在数字通信中,用来描述网络或线路理论上传输数据的最高速率。\n\n常用带宽单位:bit/s(bps)、kb/s(kbps)、Mb/s(=$10^6$ b/s)、 Gb/s(=$10^9$ b/s)、Tb/s(=$10^{12}$ b/s)……生活中为了方便常常省略后面的“b/s”,比如“线路带宽是10M”,实际指的是 10Mb/s.\n\n\n\n### 时延\n\n\n\n时延是指一个报文或分组从一个网络的一端传送到另一个端所需要的时间。它包括了发送时延,传播时延,处理时延。(时延=发送时延+传播时延+处理时延)一般,发送时延与传播时延是我们主要考虑的。对于报文长度较大的情况,发送时延是主要矛盾;报文长度较小的情况,传播时延是主要矛盾。\n\n\n\n发送时延:指结点在发送数据时使数据块从结点进入到传输媒体所需的时间,也就是从数据块的第一个bit开始发送算起,到最后一个bit发送完毕所需的时间。\n\n发送时延=数据帧长度/发送速率\n\n\n\n传播时延:指电磁信号或光信号在传输介质中传播一定的距离所花费的时间,即从发送端发送数据开始,到接收端收到数据(或者从接收端发送确认帧,到发送端收到确认帧),总共经历的时间。\n\n\n\n处理时延:指数据在交换结点为了存储转发而进行一些必要的处理所花费的时间,在结点缓存队列中,分组排队所经历的时延是处理时延的重要组成部分。\n\n\n\n\n\n\n\n","slug":"网络通信/计算机网络的产生","published":1,"updated":"2021-09-13T02:00:10.634Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6ri008kakve8zmh4jwv","content":"

计算机网络的产生

分组交换网的出现

分组交换技术是在1960年代末出现的,当时美国高级研究计划局(简称ARPA)为实现远程计算机之间的信息交换,资助建设一个试验性的网络,该网络被称为ARPANET。 ARPANET的主要研究成果之一就是开发一种新的网络协议 ,在 ARPANET 网络上对话必须使用这种网络协议。

\n

局域网、城域网、广域网

局域网

1、局域网定义

\n

局域网(Local Area Network),简称LAN,是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。

\n

2、局域网介绍

\n

局域网(Local Area Network,LAN)是在一个局部的地理范围内(如一个学校、工厂和机关内),一般是方圆几千米以内,将各种计算机,外部设备和数据库等互相联接起来组成的计算机通信网。它可以通过数据通信网或专用数据电路,与远方的局域网、数据库或处理中心相连接,构成一个较大范围的信息处理系统。局域网可以实现文件管理、应用软件共享、打印机共享、扫描仪共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网严格意义上是封闭型的。它可以由办公室内几台甚至上千上万台计算机组成。决定局域网的主要技术要素为:网络拓扑,传输介质与介质访问控制方法。

\n

3、局域网组成

\n

局域网由硬件(包括服务器、工作站、打印机、网卡、互联设备等)和网络传输介质(光纤和WIFI等),以及软件所组成。

\n

4、局域网拓扑结构

\n

星形、树形、总线形和环形。

\n

5、局域网四种技术类型/标准

\n

以太网(Ethernet);令牌环(Token Ring);令牌总线(Token Bus),如ARCNET;光纤分布式数据接口(FDDI)。目前,以太网(Ethernet)类型的局域网应用最为广泛。

\n

城域网

1、城域网定义

\n

城域网(Metropolitan Area Network),简称MAN,是在一个城市范围内所建立的计算机通信网。属宽带局域网。由于采用具有有源交换元件的局域网技术,网中传输时延较小,它的传输媒介主要采用光缆,传输速率在100兆比特/秒以上。

\n

2、城域网介绍

\n

城域网MAN是基于一种大型的局域网LAN,通常使用与局域网LAN相似的技术。将城域网单独列出来的一个主要原因是它有自己的一个标准:分布式队列双总线DQDB(Distributed Queue Dual Bus),即IEEE802.6。DQDB是由双总线构成,所有的计算机都连结在上面。

\n

MAN的一个重要用途是用作骨干网,通过它将位于同一城市内不同地点的主机、数据库,以及LAN等互相联接起来,这与WAN的作用有相似之处,但两者在实现方法与性能上有很大差别。

\n

建设局域网或广域网包括资源子网和通信子网两个方面,而城域网的建设主要集中在通信子网上,其中也包含两个方面:

\n
    \n
  • 一是城市骨干网,它与中国的骨干网相连。

    \n
  • \n
  • 二是城市接入网,它把本地所有的联网用户与城市骨干网相连。

    \n
  • \n
\n

广域网

广域网(Wide Area Network),简称 WAN,又称广域网、外网、公网(相对应,局域网LAN也称内网、私网)。是连接不同地区局域网城域网计算机通信的远程网。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。广域网并不等同于互联网

\n

广域网是网络专业的一个专业术语,通常特指跨接很大物理范围的计算机网络,一般要超过几十公里,比如某公司总部在北京,分公司在上海,这两个地方的分别有各自的局域网,这两个局域网通过自建或者租用通讯线路的方式连接起来就构成了一个广域网络。互联网也可以说是广域网的一个实例

\n

计算机体系结构形成

第一阶段:从单个网络(分组交换网ARPANET)->互联网

\n

第二阶段:三级结构因特网的形成【国家主干网->地区网->校园网】

\n

第三阶段:多级结构因特网

\n

ISP: 因特网服务供应商

\n

NAP: 网络接入点

\n
\n

计算机网络组成与结构

结构与功能密不可分,计算机网络具有两大功能:数据处理与数据通信,因此计算机组成结构可以据此分为资源子网、通信子网

\n

资源子网:主计算机 + 终端

\n

通信子网:通信控制处理机 + 通信线路

\n

计算机网络性能指标

带宽

带宽一词最初指的是电磁波频带的宽度,也就是信号的最高频率与最低频率的差值。目前,它被更广泛地借用在数字通信中,用来描述网络或线路理论上传输数据的最高速率。

\n

常用带宽单位:bit/s(bps)、kb/s(kbps)、Mb/s(=$10^6$ b/s)、 Gb/s(=$10^9$ b/s)、Tb/s(=$10^{12}$ b/s)……生活中为了方便常常省略后面的“b/s”,比如“线路带宽是10M”,实际指的是 10Mb/s.

\n

时延

时延是指一个报文或分组从一个网络的一端传送到另一个端所需要的时间。它包括了发送时延,传播时延,处理时延。(时延=发送时延+传播时延+处理时延)一般,发送时延与传播时延是我们主要考虑的。对于报文长度较大的情况,发送时延是主要矛盾;报文长度较小的情况,传播时延是主要矛盾。

\n

发送时延:指结点在发送数据时使数据块从结点进入到传输媒体所需的时间,也就是从数据块的第一个bit开始发送算起,到最后一个bit发送完毕所需的时间。

\n

发送时延=数据帧长度/发送速率

\n

传播时延:指电磁信号或光信号在传输介质中传播一定的距离所花费的时间,即从发送端发送数据开始,到接收端收到数据(或者从接收端发送确认帧,到发送端收到确认帧),总共经历的时间。

\n

处理时延:指数据在交换结点为了存储转发而进行一些必要的处理所花费的时间,在结点缓存队列中,分组排队所经历的时延是处理时延的重要组成部分。

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

计算机网络的产生

分组交换网的出现

分组交换技术是在1960年代末出现的,当时美国高级研究计划局(简称ARPA)为实现远程计算机之间的信息交换,资助建设一个试验性的网络,该网络被称为ARPANET。 ARPANET的主要研究成果之一就是开发一种新的网络协议 ,在 ARPANET 网络上对话必须使用这种网络协议。

\n

局域网、城域网、广域网

局域网

1、局域网定义

\n

局域网(Local Area Network),简称LAN,是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。

\n

2、局域网介绍

\n

局域网(Local Area Network,LAN)是在一个局部的地理范围内(如一个学校、工厂和机关内),一般是方圆几千米以内,将各种计算机,外部设备和数据库等互相联接起来组成的计算机通信网。它可以通过数据通信网或专用数据电路,与远方的局域网、数据库或处理中心相连接,构成一个较大范围的信息处理系统。局域网可以实现文件管理、应用软件共享、打印机共享、扫描仪共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网严格意义上是封闭型的。它可以由办公室内几台甚至上千上万台计算机组成。决定局域网的主要技术要素为:网络拓扑,传输介质与介质访问控制方法。

\n

3、局域网组成

\n

局域网由硬件(包括服务器、工作站、打印机、网卡、互联设备等)和网络传输介质(光纤和WIFI等),以及软件所组成。

\n

4、局域网拓扑结构

\n

星形、树形、总线形和环形。

\n

5、局域网四种技术类型/标准

\n

以太网(Ethernet);令牌环(Token Ring);令牌总线(Token Bus),如ARCNET;光纤分布式数据接口(FDDI)。目前,以太网(Ethernet)类型的局域网应用最为广泛。

\n

城域网

1、城域网定义

\n

城域网(Metropolitan Area Network),简称MAN,是在一个城市范围内所建立的计算机通信网。属宽带局域网。由于采用具有有源交换元件的局域网技术,网中传输时延较小,它的传输媒介主要采用光缆,传输速率在100兆比特/秒以上。

\n

2、城域网介绍

\n

城域网MAN是基于一种大型的局域网LAN,通常使用与局域网LAN相似的技术。将城域网单独列出来的一个主要原因是它有自己的一个标准:分布式队列双总线DQDB(Distributed Queue Dual Bus),即IEEE802.6。DQDB是由双总线构成,所有的计算机都连结在上面。

\n

MAN的一个重要用途是用作骨干网,通过它将位于同一城市内不同地点的主机、数据库,以及LAN等互相联接起来,这与WAN的作用有相似之处,但两者在实现方法与性能上有很大差别。

\n

建设局域网或广域网包括资源子网和通信子网两个方面,而城域网的建设主要集中在通信子网上,其中也包含两个方面:

\n
    \n
  • 一是城市骨干网,它与中国的骨干网相连。

    \n
  • \n
  • 二是城市接入网,它把本地所有的联网用户与城市骨干网相连。

    \n
  • \n
\n

广域网

广域网(Wide Area Network),简称 WAN,又称广域网、外网、公网(相对应,局域网LAN也称内网、私网)。是连接不同地区局域网城域网计算机通信的远程网。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。广域网并不等同于互联网

\n

广域网是网络专业的一个专业术语,通常特指跨接很大物理范围的计算机网络,一般要超过几十公里,比如某公司总部在北京,分公司在上海,这两个地方的分别有各自的局域网,这两个局域网通过自建或者租用通讯线路的方式连接起来就构成了一个广域网络。互联网也可以说是广域网的一个实例

\n

计算机体系结构形成

第一阶段:从单个网络(分组交换网ARPANET)->互联网

\n

第二阶段:三级结构因特网的形成【国家主干网->地区网->校园网】

\n

第三阶段:多级结构因特网

\n

ISP: 因特网服务供应商

\n

NAP: 网络接入点

\n
\n

计算机网络组成与结构

结构与功能密不可分,计算机网络具有两大功能:数据处理与数据通信,因此计算机组成结构可以据此分为资源子网、通信子网

\n

资源子网:主计算机 + 终端

\n

通信子网:通信控制处理机 + 通信线路

\n

计算机网络性能指标

带宽

带宽一词最初指的是电磁波频带的宽度,也就是信号的最高频率与最低频率的差值。目前,它被更广泛地借用在数字通信中,用来描述网络或线路理论上传输数据的最高速率。

\n

常用带宽单位:bit/s(bps)、kb/s(kbps)、Mb/s(=$10^6$ b/s)、 Gb/s(=$10^9$ b/s)、Tb/s(=$10^{12}$ b/s)……生活中为了方便常常省略后面的“b/s”,比如“线路带宽是10M”,实际指的是 10Mb/s.

\n

时延

时延是指一个报文或分组从一个网络的一端传送到另一个端所需要的时间。它包括了发送时延,传播时延,处理时延。(时延=发送时延+传播时延+处理时延)一般,发送时延与传播时延是我们主要考虑的。对于报文长度较大的情况,发送时延是主要矛盾;报文长度较小的情况,传播时延是主要矛盾。

\n

发送时延:指结点在发送数据时使数据块从结点进入到传输媒体所需的时间,也就是从数据块的第一个bit开始发送算起,到最后一个bit发送完毕所需的时间。

\n

发送时延=数据帧长度/发送速率

\n

传播时延:指电磁信号或光信号在传输介质中传播一定的距离所花费的时间,即从发送端发送数据开始,到接收端收到数据(或者从接收端发送确认帧,到发送端收到确认帧),总共经历的时间。

\n

处理时延:指数据在交换结点为了存储转发而进行一些必要的处理所花费的时间,在结点缓存队列中,分组排队所经历的时延是处理时延的重要组成部分。

\n"},{"title":"古典加密算法","date":"2021-09-14T15:13:59.000Z","katex":true,"abbrlink":"ee4b2b4d","description":"古典加密算法了解,置换加密,替代加密","cover":"https://gitee.com/ajream/images/raw/master/img/20210914232714_web-save.png","_content":"\n## 置换加密\n\n**置换密码算法**的原理是不改变明文字符,只将字符在明文中的排列顺序改变,从而实现明文信息的加密。置换密码有时又称为换位密码。\n**矩阵换位法**是实现置换密码的一种常用方法。它将明文中的字母按照给的顺序安排在一个矩阵中,然后用根据密钥提供的顺序重新组合矩阵中字母,从而形成密文。\n\n\n## 替代加密\n\n替代密码算法的原理是使用替代法进行加密,就是将明文中的字符用其它字符替代后形成密文。例如:明文字母 a,b,c,d ,用 D,E,F,G做对应替换后形成密文。 \n\n替代密码包括多种类型,如单表替代密码,多明码替代密码,多字母替代密码,多表替代密码等。下面我们介绍一种典型的单表替代密码,恺撒(caesar)密码,又叫循环移位密码。它的加密方法就是将明文中的每个字母用此字符在字母表中后面第 k 个字母替代。\n\n它的加密过程可以表示为下面的函数:\n\nE(m) = (m+k) mod n\n\n其中:\n- m 为明文字母在字母表中的位置数; \n- n 为字母表中的字母个数; \n- k 为密钥;\n- E(m) 为密文字母在字母表中对应的位置数。\n\n例如,对于明文字母 H,其在字母表中的位置数为 8,设 k=4,则按照上式计算出 来的密文为 L:\n\nE(8) = (m+k) mod n = (8+4) mod 26 = 12 = L","source":"_posts/网络安全/古典加密算法.md","raw":"---\ntitle: 古典加密算法\ndate: '2021-09-14 23:13:59'\nkatex: true\nabbrlink: ee4b2b4d\ntags:\n - 网络安全\ncategories:\n - 网络安全\ndescription: 古典加密算法了解,置换加密,替代加密\ncover: https://gitee.com/ajream/images/raw/master/img/20210914232714_web-save.png\n---\n\n## 置换加密\n\n**置换密码算法**的原理是不改变明文字符,只将字符在明文中的排列顺序改变,从而实现明文信息的加密。置换密码有时又称为换位密码。\n**矩阵换位法**是实现置换密码的一种常用方法。它将明文中的字母按照给的顺序安排在一个矩阵中,然后用根据密钥提供的顺序重新组合矩阵中字母,从而形成密文。\n\n\n## 替代加密\n\n替代密码算法的原理是使用替代法进行加密,就是将明文中的字符用其它字符替代后形成密文。例如:明文字母 a,b,c,d ,用 D,E,F,G做对应替换后形成密文。 \n\n替代密码包括多种类型,如单表替代密码,多明码替代密码,多字母替代密码,多表替代密码等。下面我们介绍一种典型的单表替代密码,恺撒(caesar)密码,又叫循环移位密码。它的加密方法就是将明文中的每个字母用此字符在字母表中后面第 k 个字母替代。\n\n它的加密过程可以表示为下面的函数:\n\nE(m) = (m+k) mod n\n\n其中:\n- m 为明文字母在字母表中的位置数; \n- n 为字母表中的字母个数; \n- k 为密钥;\n- E(m) 为密文字母在字母表中对应的位置数。\n\n例如,对于明文字母 H,其在字母表中的位置数为 8,设 k=4,则按照上式计算出 来的密文为 L:\n\nE(8) = (m+k) mod n = (8+4) mod 26 = 12 = L","slug":"网络安全/古典加密算法","published":1,"updated":"2021-09-16T14:31:54.376Z","_id":"cktk8o6rj008oakvef3va6zbp","comments":1,"layout":"post","photos":[],"link":"","content":"

置换加密

\n

置换密码算法的原理是不改变明文字符,只将字符在明文中的排列顺序改变,从而实现明文信息的加密。置换密码有时又称为换位密码。
\n矩阵换位法是实现置换密码的一种常用方法。它将明文中的字母按照给的顺序安排在一个矩阵中,然后用根据密钥提供的顺序重新组合矩阵中字母,从而形成密文。

\n

替代加密

\n

替代密码算法的原理是使用替代法进行加密,就是将明文中的字符用其它字符替代后形成密文。例如:明文字母 a,b,c,d ,用 D,E,F,G做对应替换后形成密文。

\n

替代密码包括多种类型,如单表替代密码,多明码替代密码,多字母替代密码,多表替代密码等。下面我们介绍一种典型的单表替代密码,恺撒(caesar)密码,又叫循环移位密码。它的加密方法就是将明文中的每个字母用此字符在字母表中后面第 k 个字母替代。

\n

它的加密过程可以表示为下面的函数:

\n

E(m) = (m+k) mod n

\n

其中:

\n
    \n
  • m 为明文字母在字母表中的位置数;
  • \n
  • n 为字母表中的字母个数;
  • \n
  • k 为密钥;
  • \n
  • E(m) 为密文字母在字母表中对应的位置数。
  • \n
\n

例如,对于明文字母 H,其在字母表中的位置数为 8,设 k=4,则按照上式计算出 来的密文为 L:

\n

E(8) = (m+k) mod n = (8+4) mod 26 = 12 = L

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

置换加密

\n

置换密码算法的原理是不改变明文字符,只将字符在明文中的排列顺序改变,从而实现明文信息的加密。置换密码有时又称为换位密码。
\n矩阵换位法是实现置换密码的一种常用方法。它将明文中的字母按照给的顺序安排在一个矩阵中,然后用根据密钥提供的顺序重新组合矩阵中字母,从而形成密文。

\n

替代加密

\n

替代密码算法的原理是使用替代法进行加密,就是将明文中的字符用其它字符替代后形成密文。例如:明文字母 a,b,c,d ,用 D,E,F,G做对应替换后形成密文。

\n

替代密码包括多种类型,如单表替代密码,多明码替代密码,多字母替代密码,多表替代密码等。下面我们介绍一种典型的单表替代密码,恺撒(caesar)密码,又叫循环移位密码。它的加密方法就是将明文中的每个字母用此字符在字母表中后面第 k 个字母替代。

\n

它的加密过程可以表示为下面的函数:

\n

E(m) = (m+k) mod n

\n

其中:

\n
    \n
  • m 为明文字母在字母表中的位置数;
  • \n
  • n 为字母表中的字母个数;
  • \n
  • k 为密钥;
  • \n
  • E(m) 为密文字母在字母表中对应的位置数。
  • \n
\n

例如,对于明文字母 H,其在字母表中的位置数为 8,设 k=4,则按照上式计算出 来的密文为 L:

\n

E(8) = (m+k) mod n = (8+4) mod 26 = 12 = L

\n"},{"title":"51单片机学习笔记(五)","description":"逐次逼近式AD转换器原理介绍,PCF8591模块介绍","abbrlink":"c4d68191","date":"2021-08-19T11:02:48.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png","_content":"\n\n\n\n\n\n\n## 逐次逼近式AD转换器\n\n\n\n这是一个基本原理图\n\n首先,内部产生一个参考电压Vref,然后与要进行测量的电压Vin进行比较,如果Vref<=Vin,就产生一个“1”信号(否则产生“0”信号),并存储到N位寄存器的最高位,然重新生成参考电压信号Vref = Vref+0.5Vref,(如果前面产生的是“0”信号,则新的参考电压Vref = 0.5Vref),再与Vin比较,将比较结果(“0”或“1”存到寄存器的下一位),循环往复,直到N位寄存器全部存满数据,最后通过锁存器输出转化后的数字信号。\n\n![image-20210824143628336](https://gitee.com/ajream/images/raw/master/img/20210824143631_image-20210824143628336.png)\n\n比如:Vin = 3.75V, Vref = 2.5V\n\n1. Vref=2.5 < 3.75,因此寄存器最高位存储数据1,然后重新生成Vref = 2.5+0.5*2.5 = 3.75\n2. 此时Vref=3.75 == Vin, 返回比较结果1, 存储下来, 然后重新生成Vref = 3.75+3.75*0.5 = 5.6\n3. 此时Vref=5.6 > Vin,返回比较结果0,存储下来。。。。。\n4. 最后结果是:`1100 0000`\n\n\n\n\n\n## PCF8591模块\n\n\n\n### 简介\n\nPCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入、一个输出和一个串行 I2C 总线接口;\n\n3个地址引脚A0、A1 和 A2 用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件;\n\nPCF8591由于其使用的简单方便和集成度高,在单片机应用系统中得到了广泛的应用。\n\n特点:\n\n1. 单电源供电\n\n2. 工作电压:2.5 V ~ 6 V\n\n3. I2C总线串行输入/输出\n\n4. 通过3个硬件地址引脚编址\n\n5. 采样速率取决于 I2C 总线传输速率决定\n\n6. 4个模拟输入可编程为单端或差分输入\n\n7. 自动增量通道选择\n\n8. 8位逐次比较型 A/D 转换\n\n\n\n管脚定义:\n\n![image-20210824145451094](https://gitee.com/ajream/images/raw/master/img/20210824145452_image-20210824145451094.png)\n\n1. AIN0~AIN3:模拟量输入通道\n2. AOUT:模拟输出通道\n3. A0~A2:硬件设备地址\n4. VDD:电源正极\n5. VSS:电源负极\n6. VREF:参考电压输入。\n7. EXT:振荡器输入时,内部/外部的切换开关。\n8. OSC:振荡器输入/输出。\n9. SCL:I2C BUS时钟输入。\n10. SDA:I2C BUS 数据输入/输出。\n11. AGND:模拟地,模拟信号和基准电源的参考地\n\n\n\n\n\n开发板中的PCF8591接线:\n\n![image-20210824145815414](https://gitee.com/ajream/images/raw/master/img/20210824145816_image-20210824145815414.png)\n\n\n\n\n\n### PCF8591地址\n\nI2C 总线系统中的每一片PCF8591 通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分\n\n可编程部分必须根据地址引脚A0、A1 和 A2 来设置,因此I2C系统中最多可接8个PCF8591\n\n在I2C 总线协议中地址必须是起始条件后作为第一个字节发送。\n\n地址字节的最后一位是用于设置以后数据传输方向的读/写位(1为读操作,0为写操作)\n\n\n\n![image-20210824150249772](https://gitee.com/ajream/images/raw/master/img/20210824150251_image-20210824150249772.png)\n\n\n\n\n\n### PCF8591控制字节\n\n发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。\n\n控制寄存器的高半字节用于允许模拟输出,以及可以将模拟输入编程为单端或差分输入。\n\n低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置1,每次A/D 转换后通道号将自动增加。\n\n\n\n\n\n\n\n\n\n## 例子\n\n### (一)\n\n利用I2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,通过数码管显示\n\n```c\n/*********************************************************************************\n* 【作 者】:\t清翔电子:向量\n* 【版 本】:\tV1.0\n* 【网 站】:\thttp://www.qxmcu.com/ \n* 【淘宝店铺】:\thttp://qxmcu.taobao.com/\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,数码管\n\t\t\t\t\t显示。\t\t \t\t\t \t\t\t \n* 【使用说明】: \t用手转动AD旁边的电位器头,数码管数值会随之变化。\n**********************************************************************************/\n#include \n#include \n\n#define PCF8591ADDR 0X90 //PCF8591地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n#define\tCH0 0\t\t\t //AD通道0\n#define\tCH1\t1\t\t\t //AD通道1\n#define\tCH2\t2\t\t\t //AD通道2\n#define\tCH3\t3\t\t\t //AD通道3\n#define\tDAout\t0x40\t //DA输出命令\n\n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n使用typedef给已有数据类型取别名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\ntypedef unsigned char u8;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\ntypedef unsigned int u16;\n\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1; //I2C时钟总线\nsbit SDA = P2^0; //I2C数据总线\nuint num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\n//数码管位选码 //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位 \nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数名\t:void delay(INT16U ms)\n参数\t:ms,毫秒延时形参\n返回值\t:无\n描述\t:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid delay(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n/*====================================\n函数\t:void delay5us()\n参数\t:无\n返回值\t:无\n描述\t:12T 51单片机5微秒延时函数自适应时钟\n====================================*/\nvoid delay5us()\n{\n\t#if MAIN_Fosc == 11059200\n\t\t_nop_();\n\t#elif MAIN_Fosc == 12000000\n\t\t_nop_();\n\t#elif MAIN_Fosc == 22118400\n\t\t_nop_(); _nop_(); _nop_();\n\t#endif\n}\n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示变量取值0-65535 \n返回值\t:无\n描述\t:数码管动态显示函数第一位显示小数点\n====================================*/\n//void display(uint i)\n//{\n//\tstatic uchar wei;\n//\t \t\t\n//\tP0 = 0XFF;//清除断码\n//\tWE = 1;//打开位选锁存器\n//\tP0 = SMGwei[wei];\n//\tWE = 0;//锁存位选数据\n//\n//\tswitch(wei)\n//\t{\n//\t\tcase 0: DU = 1; P0 = SMGduan[i / 1000] | 0x80; \tDU = 0; break;//千位显示小数点\n//\t\tcase 1: DU = 1; P0 = SMGduan[i % 1000 / 100]; \tDU = 0; break;//显示百位\n//\t\tcase 2: DU = 1; P0 = SMGduan[i % 100 / 10]; \tDU = 0; break;//显示十位\t\n//\t\tcase 3: DU = 1; P0 = SMGduan[i % 10]; \t\t\tDU = 0; break;//显示个位\t\t\n//\t}\n//\twei++;\n//\tif(wei == 4)\n//\t\twei = 0;\n//}\n\n/*====================================\n函数:display(INT16U Value)\n参数:Value,显示值 取值0-65535\n描述:共阴极数码管显示函数可显示一个字节的数\n====================================*/\nvoid display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型\n{\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\tP0 = SMGduan[Value/1000]|0x80;\t//数码管显示千位\n\tDU = 1;\t\t\t\t\t\t\t//打开段选\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tP0 = SMGwei[0];\t\t\t\t //第一位数码管\n\tWE = 1;\t\t\t\t\t\t//打开位选\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%1000/100]; //显示百位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[1];\t\t\t //第二位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%100/10];\t\t//显示十位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[2];\t\t\t\t//第三位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%10];\t\t//显示个位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[3];\t\t\t\t//第四位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n}\n\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart()\n{\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop()\n{\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK()\n{\n\tSCL = 0;//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;//拉高,读SDA\n\tdelay5us();\n\tif(SDA)//NOACK\n\t{\n\t\tSCL = 0;\n\t\treturn(1);//返回1\n\t}\n\telse//ACK\n\t{\n\t\tSCL = 0;\n\t\treturn(0);//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i)\n{\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT)\n{\n\tuchar i; \n\tfor(i=0; i<8; i++) //分别写8次,每次写1位\n\t{\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:Pcf8591DA(uchar Ctrl, DAT)\n参数\t:Ctrl 8591控制字节,DAT 要写入的数据\n返回值\t:无\n描述\t:PCF8591数字量转模拟量输出\n====================================*/\n//void Pcf8591DA(uchar Ctrl, DAT)\n//{\n//\tI2cStart();//I2C起始信号\n//\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n//\tif(ReadACK()) //读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cSendByte(Ctrl);//发送控制字节\n//\tif(ReadACK())//读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cSendByte(DAT);//发送一字节数据\n//\tif(ReadACK())//读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cStop();\t//I2C停止信号\n//}\n\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte()\n{\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++)//分别读8次,每次读一位\n\t{\n\t\tDAT <<= 1; //数据左移1位,准备接收一位\n\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); //返回读出的数据\n}\n\n/*====================================\n函数\t:PCF8591Read(uchar Ctrl)\n参数\t:Ctrl 8591控制字节\n返回值\t:AD转出的数字量\n描述\t:读指定通道的输入的模拟量专为数字量\n====================================*/\nuchar PCF8591Read(uchar Ctrl)\n{\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环\n{\t\n\twhile(1)\n\t{\n\t\tnum = (PCF8591Read(CH2) * 19.53 + 0.5);//读AD通道2,电位器值\n\t\t// 5000mv/256约等于等于19.53 ,加0.5是小数四舍五入接近实际转换精度\n\t\tdisplay(num); //数码管显示函数\n\t\tdelay(5);\n\t}\n} \n\n```\n\n\n\n### (二)\n\n使用I2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10亮度变化\n\n```c\n/*********************************************************************************\n* 【作 者】:\t清翔电子:向量\n* 【版 本】:\tV1.0\n* 【网 站】:\thttp://www.qxmcu.com/ \n* 【淘宝店铺】:\thttp://qxmcu.taobao.com/\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10\t\t \t\t\t \t\t\t \n* 【使用说明】: \t随着环境光线的变强LED10亮度增加,环境光变弱LED10亮度变暗\n\t\t\t\t 需要用跳线帽把DEN和DOUT短接(默认已短接)\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n#define PCF8591ADDR 0X90 //PCF8591硬件地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n#define\tCH0 0\t\t\t //AD通道0\n#define\tCH1\t1\t\t\t //AD通道1\n#define\tCH2\t2\t\t\t //AD通道2\n#define\tCH3\t3\t\t\t //AD通道3\n#define\tDAout\t0x40\t //DA输出命令\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1; //I2C时钟总线\nsbit SDA = P2^0; //I2C数据总线\nuchar num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n/*====================================\n函数\t: delay(uint z)\n参数\t:z 延时毫秒设定,取值范围0-65535\n返回值\t:无\n描述\t:12T/Fosc11.0592M毫秒级延时\n====================================*/\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示数值,取值范围0-255\n返回值\t:无\n描述\t:三位共阴数码管动态显示\n====================================*/\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid timer0Init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:delay5us()\n参数\t:无\n返回值\t:无\n描述\t:5us延时函数\n====================================*/\nvoid delay5us()\n{\n\t_nop_();\n}\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart()\n{\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop()\n{\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK()\n{\n\tSCL = 0;//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;//拉高,读SDA\n\tdelay5us();\n\tif(SDA)//NOACK\n\t{\n\t\tSCL = 0;\n\t\treturn(1);//返回1\n\t}\n\telse//ACK\n\t{\n\t\tSCL = 0;\n\t\treturn(0);//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i)\n{\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT)\n{\n\tuchar i; \n\tfor(i=0; i<8; i++) //分别写8次,每次写1位\n\t{\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:Pcf8591DA(uchar Ctrl, DAT)\n参数\t:Ctrl 8591控制字节,DAT 要写入的数据\n返回值\t:无\n描述\t:PCF8591数字量转模拟量输出\n====================================*/\nvoid Pcf8591DA(uchar Ctrl, DAT)\n{\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK()) //读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(DAT);//发送一字节数据\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cStop();\t//I2C停止信号\n}\n\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte()\n{\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++)//分别读8次,每次读一位\n\t{\n\t\tDAT <<= 1; //数据左移1位,准备接收一位\n\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); //返回读出的数据\n}\n\n/*====================================\n函数\t:PCF8591Read(uchar Ctrl)\n参数\t:Ctrl 8591控制字节\n返回值\t:AD转出的数字量\n描述\t:读指定通道的输入的模拟量专为数字量\n====================================*/\nuchar PCF8591Read(uchar Ctrl)\n{\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环\n{\t\n\ttimer0Init();//定时器0初始化\n\twhile(1)\n\t{\n\t\tEA = 0;//屏蔽中断\n\t\tnum = PCF8591Read(CH0);//读AD通道0,光敏值\n\t\tPcf8591DA(DAout, ~num);//把光敏转出的数字量取反,输出模拟量控制LED10\n\t\tEA = 1;//开中断\n\t\tdelay(5);\n\t}\n} \n\n//定时器0中断函数\nvoid timer0() interrupt 1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n} \n```\n\n","source":"_posts/51单片机/51单片机学习笔记(五).md","raw":"---\ntitle: 51单片机学习笔记(五)\ntags:\n - 51单片机\ncategories:\n - 硬件学习\n - 51单片机\ndescription: 逐次逼近式AD转换器原理介绍,PCF8591模块介绍\nabbrlink: c4d68191\ndate: 2021-08-19 19:02:48\ncover: https://gitee.com/ajream/images/raw/master/img/20210910204417_51cover.png\n---\n\n\n\n\n\n\n\n## 逐次逼近式AD转换器\n\n\n\n这是一个基本原理图\n\n首先,内部产生一个参考电压Vref,然后与要进行测量的电压Vin进行比较,如果Vref<=Vin,就产生一个“1”信号(否则产生“0”信号),并存储到N位寄存器的最高位,然重新生成参考电压信号Vref = Vref+0.5Vref,(如果前面产生的是“0”信号,则新的参考电压Vref = 0.5Vref),再与Vin比较,将比较结果(“0”或“1”存到寄存器的下一位),循环往复,直到N位寄存器全部存满数据,最后通过锁存器输出转化后的数字信号。\n\n![image-20210824143628336](https://gitee.com/ajream/images/raw/master/img/20210824143631_image-20210824143628336.png)\n\n比如:Vin = 3.75V, Vref = 2.5V\n\n1. Vref=2.5 < 3.75,因此寄存器最高位存储数据1,然后重新生成Vref = 2.5+0.5*2.5 = 3.75\n2. 此时Vref=3.75 == Vin, 返回比较结果1, 存储下来, 然后重新生成Vref = 3.75+3.75*0.5 = 5.6\n3. 此时Vref=5.6 > Vin,返回比较结果0,存储下来。。。。。\n4. 最后结果是:`1100 0000`\n\n\n\n\n\n## PCF8591模块\n\n\n\n### 简介\n\nPCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入、一个输出和一个串行 I2C 总线接口;\n\n3个地址引脚A0、A1 和 A2 用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件;\n\nPCF8591由于其使用的简单方便和集成度高,在单片机应用系统中得到了广泛的应用。\n\n特点:\n\n1. 单电源供电\n\n2. 工作电压:2.5 V ~ 6 V\n\n3. I2C总线串行输入/输出\n\n4. 通过3个硬件地址引脚编址\n\n5. 采样速率取决于 I2C 总线传输速率决定\n\n6. 4个模拟输入可编程为单端或差分输入\n\n7. 自动增量通道选择\n\n8. 8位逐次比较型 A/D 转换\n\n\n\n管脚定义:\n\n![image-20210824145451094](https://gitee.com/ajream/images/raw/master/img/20210824145452_image-20210824145451094.png)\n\n1. AIN0~AIN3:模拟量输入通道\n2. AOUT:模拟输出通道\n3. A0~A2:硬件设备地址\n4. VDD:电源正极\n5. VSS:电源负极\n6. VREF:参考电压输入。\n7. EXT:振荡器输入时,内部/外部的切换开关。\n8. OSC:振荡器输入/输出。\n9. SCL:I2C BUS时钟输入。\n10. SDA:I2C BUS 数据输入/输出。\n11. AGND:模拟地,模拟信号和基准电源的参考地\n\n\n\n\n\n开发板中的PCF8591接线:\n\n![image-20210824145815414](https://gitee.com/ajream/images/raw/master/img/20210824145816_image-20210824145815414.png)\n\n\n\n\n\n### PCF8591地址\n\nI2C 总线系统中的每一片PCF8591 通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分\n\n可编程部分必须根据地址引脚A0、A1 和 A2 来设置,因此I2C系统中最多可接8个PCF8591\n\n在I2C 总线协议中地址必须是起始条件后作为第一个字节发送。\n\n地址字节的最后一位是用于设置以后数据传输方向的读/写位(1为读操作,0为写操作)\n\n\n\n![image-20210824150249772](https://gitee.com/ajream/images/raw/master/img/20210824150251_image-20210824150249772.png)\n\n\n\n\n\n### PCF8591控制字节\n\n发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。\n\n控制寄存器的高半字节用于允许模拟输出,以及可以将模拟输入编程为单端或差分输入。\n\n低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置1,每次A/D 转换后通道号将自动增加。\n\n\n\n\n\n\n\n\n\n## 例子\n\n### (一)\n\n利用I2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,通过数码管显示\n\n```c\n/*********************************************************************************\n* 【作 者】:\t清翔电子:向量\n* 【版 本】:\tV1.0\n* 【网 站】:\thttp://www.qxmcu.com/ \n* 【淘宝店铺】:\thttp://qxmcu.taobao.com/\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,数码管\n\t\t\t\t\t显示。\t\t \t\t\t \t\t\t \n* 【使用说明】: \t用手转动AD旁边的电位器头,数码管数值会随之变化。\n**********************************************************************************/\n#include \n#include \n\n#define PCF8591ADDR 0X90 //PCF8591地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n#define\tCH0 0\t\t\t //AD通道0\n#define\tCH1\t1\t\t\t //AD通道1\n#define\tCH2\t2\t\t\t //AD通道2\n#define\tCH3\t3\t\t\t //AD通道3\n#define\tDAout\t0x40\t //DA输出命令\n\n#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ\n/*====================================\n使用typedef给已有数据类型取别名\n====================================*/\ntypedef unsigned char INT8U;\ntypedef unsigned char uchar;\ntypedef unsigned char u8;\n\ntypedef unsigned int INT16U;\ntypedef unsigned int uint;\ntypedef unsigned int u16;\n\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1; //I2C时钟总线\nsbit SDA = P2^0; //I2C数据总线\nuint num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n\n//数码管位选码 //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位 \nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码\n\n/*====================================\n函数名\t:void delay(INT16U ms)\n参数\t:ms,毫秒延时形参\n返回值\t:无\n描述\t:12T 51单片机自适应主时钟毫秒级延时函数\n====================================*/\nvoid delay(INT16U ms)\n{\n INT16U i;\n\t do{\n\t i = MAIN_Fosc / 96000; \n\t\t while(--i); //96T per loop\n }while(--ms);\n}\n/*====================================\n函数\t:void delay5us()\n参数\t:无\n返回值\t:无\n描述\t:12T 51单片机5微秒延时函数自适应时钟\n====================================*/\nvoid delay5us()\n{\n\t#if MAIN_Fosc == 11059200\n\t\t_nop_();\n\t#elif MAIN_Fosc == 12000000\n\t\t_nop_();\n\t#elif MAIN_Fosc == 22118400\n\t\t_nop_(); _nop_(); _nop_();\n\t#endif\n}\n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示变量取值0-65535 \n返回值\t:无\n描述\t:数码管动态显示函数第一位显示小数点\n====================================*/\n//void display(uint i)\n//{\n//\tstatic uchar wei;\n//\t \t\t\n//\tP0 = 0XFF;//清除断码\n//\tWE = 1;//打开位选锁存器\n//\tP0 = SMGwei[wei];\n//\tWE = 0;//锁存位选数据\n//\n//\tswitch(wei)\n//\t{\n//\t\tcase 0: DU = 1; P0 = SMGduan[i / 1000] | 0x80; \tDU = 0; break;//千位显示小数点\n//\t\tcase 1: DU = 1; P0 = SMGduan[i % 1000 / 100]; \tDU = 0; break;//显示百位\n//\t\tcase 2: DU = 1; P0 = SMGduan[i % 100 / 10]; \tDU = 0; break;//显示十位\t\n//\t\tcase 3: DU = 1; P0 = SMGduan[i % 10]; \t\t\tDU = 0; break;//显示个位\t\t\n//\t}\n//\twei++;\n//\tif(wei == 4)\n//\t\twei = 0;\n//}\n\n/*====================================\n函数:display(INT16U Value)\n参数:Value,显示值 取值0-65535\n描述:共阴极数码管显示函数可显示一个字节的数\n====================================*/\nvoid display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型\n{\t\n//------------------------------\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\tP0 = SMGduan[Value/1000]|0x80;\t//数码管显示千位\n\tDU = 1;\t\t\t\t\t\t\t//打开段选\n\tDU = 0;\t\t\t\t\t\t\t//关闭段选\n\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tP0 = SMGwei[0];\t\t\t\t //第一位数码管\n\tWE = 1;\t\t\t\t\t\t//打开位选\n\tWE = 0;\t\t\t\t\t\t//关闭位选\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%1000/100]; //显示百位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[1];\t\t\t //第二位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%100/10];\t\t//显示十位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[2];\t\t\t\t//第三位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n//-------------------------------\n\tDU = 0;\n\tP0 = SMGduan[Value%10];\t\t//显示个位\n\tDU = 1;\n\tDU = 0;\n\n\tWE = 0;\n\tP0 = SMGwei[3];\t\t\t\t//第四位数码管\n\tWE = 1;\n\tWE = 0;\n\tdelay(3);\n}\n\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart()\n{\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop()\n{\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK()\n{\n\tSCL = 0;//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;//拉高,读SDA\n\tdelay5us();\n\tif(SDA)//NOACK\n\t{\n\t\tSCL = 0;\n\t\treturn(1);//返回1\n\t}\n\telse//ACK\n\t{\n\t\tSCL = 0;\n\t\treturn(0);//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i)\n{\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT)\n{\n\tuchar i; \n\tfor(i=0; i<8; i++) //分别写8次,每次写1位\n\t{\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:Pcf8591DA(uchar Ctrl, DAT)\n参数\t:Ctrl 8591控制字节,DAT 要写入的数据\n返回值\t:无\n描述\t:PCF8591数字量转模拟量输出\n====================================*/\n//void Pcf8591DA(uchar Ctrl, DAT)\n//{\n//\tI2cStart();//I2C起始信号\n//\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n//\tif(ReadACK()) //读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cSendByte(Ctrl);//发送控制字节\n//\tif(ReadACK())//读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cSendByte(DAT);//发送一字节数据\n//\tif(ReadACK())//读从机应答\n//\t\tAckFlag = 1;\t//NOACK\n//\telse\n//\t\tAckFlag = 0;\t//ACK\n//\tI2cStop();\t//I2C停止信号\n//}\n\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte()\n{\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++)//分别读8次,每次读一位\n\t{\n\t\tDAT <<= 1; //数据左移1位,准备接收一位\n\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); //返回读出的数据\n}\n\n/*====================================\n函数\t:PCF8591Read(uchar Ctrl)\n参数\t:Ctrl 8591控制字节\n返回值\t:AD转出的数字量\n描述\t:读指定通道的输入的模拟量专为数字量\n====================================*/\nuchar PCF8591Read(uchar Ctrl)\n{\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环\n{\t\n\twhile(1)\n\t{\n\t\tnum = (PCF8591Read(CH2) * 19.53 + 0.5);//读AD通道2,电位器值\n\t\t// 5000mv/256约等于等于19.53 ,加0.5是小数四舍五入接近实际转换精度\n\t\tdisplay(num); //数码管显示函数\n\t\tdelay(5);\n\t}\n} \n\n```\n\n\n\n### (二)\n\n使用I2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10亮度变化\n\n```c\n/*********************************************************************************\n* 【作 者】:\t清翔电子:向量\n* 【版 本】:\tV1.0\n* 【网 站】:\thttp://www.qxmcu.com/ \n* 【淘宝店铺】:\thttp://qxmcu.taobao.com/\n* 【实验平台】:\t清翔 QX-MCS51 单片机开发板\n* 【外部晶振】: \t11.0592mhz\t\n* 【主控芯片】: \tSTC89C52\n* 【编译环境】: \tKeil μVisio4\t\n* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10\t\t \t\t\t \t\t\t \n* 【使用说明】: \t随着环境光线的变强LED10亮度增加,环境光变弱LED10亮度变暗\n\t\t\t\t 需要用跳线帽把DEN和DOUT短接(默认已短接)\n**********************************************************************************/\n#include \n#include \n\n#define uint unsigned int\n#define uchar unsigned char\n#define PCF8591ADDR 0X90 //PCF8591硬件地址\n#define\tI2cRead 1\t\t //I2C读方向位\n#define\tI2cWrite 0\t\t //I2C写方向位\n#define\tCH0 0\t\t\t //AD通道0\n#define\tCH1\t1\t\t\t //AD通道1\n#define\tCH2\t2\t\t\t //AD通道2\n#define\tCH3\t3\t\t\t //AD通道3\n#define\tDAout\t0x40\t //DA输出命令\n\nsbit DU = P2^6;//数码管段选\nsbit WE = P2^7;//数码管段选\nsbit SCL = P2^1; //I2C时钟总线\nsbit SDA = P2^0; //I2C数据总线\nuchar num;//数码管显示的值\nbit AckFlag;//应答标志位\n\n//共阴数码管段选表0-9\nuchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};\n//数码管位选码\nuchar code SMGwei[] = {0xfe, 0xfd, 0xfb};\n\n/*====================================\n函数\t: delay(uint z)\n参数\t:z 延时毫秒设定,取值范围0-65535\n返回值\t:无\n描述\t:12T/Fosc11.0592M毫秒级延时\n====================================*/\nvoid delay(uint z)\n{\n\tuint x,y;\n\tfor(x = z; x > 0; x--)\n\t\tfor(y = 114; y > 0 ; y--); \t\t\n} \n\n/*====================================\n函数\t:display(uchar i)\n参数\t:i 显示数值,取值范围0-255\n返回值\t:无\n描述\t:三位共阴数码管动态显示\n====================================*/\nvoid display(uchar i)\n{\n\tstatic uchar wei; \t\t\n\tP0 = 0XFF;//清除断码\n\tWE = 1;//打开位选锁存器\n\tP0 = SMGwei[wei];\n\tWE = 0;//锁存位选数据\n\tswitch(wei)\n\t{\n\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;\n\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t\n\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t\n\t}\n\twei++;\n\tif(wei == 3)\n\t\twei = 0;\n}\n//定时器0初始化\nvoid timer0Init()\n{\n\tEA = 1;\t//打开总中断\n\tET0 = 1;//打开定时器0中断\n\tTR0 = 1;\t //启动定时器0\n\tTMOD |= 0X01; //定时器工作模式1,16位定时模式\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n}\n/****************************************************\nIIC通信代码\n****************************************************/\n\n/*====================================\n函数\t:delay5us()\n参数\t:无\n返回值\t:无\n描述\t:5us延时函数\n====================================*/\nvoid delay5us()\n{\n\t_nop_();\n}\n\n/*====================================\n函数\t:I2cStart()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线起始信号\n====================================*/\nvoid I2cStart()\n{\n//时钟总线为高电平期间数据总线又高变低产生起始型号\n\tSCL = 1; \n\tSDA = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 0;\n\tdelay5us();//状态保持5us \n}\n\n/*====================================\n函数\t:I2cStop()\n参数\t:无\n返回值\t:无\n描述\t:I2C总线停止信号\n====================================*/\nvoid I2cStop()\n{\n//时钟总线为高电平期间,数据总线从高变低产生终止信号\n\tSCL = 0;\n\tSDA = 0;\n\tSCL = 1;\n\tdelay5us();//状态保持5us\n\tSDA = 1;\n\tdelay5us();//状态保持5us\t\n}\n\n/*====================================\n函数\t:ReadACK()\n参数\t:无\n返回值\t:1非应答,0应答\n描述\t:I2C总线读从机应答信号\n====================================*/\nbit ReadACK()\n{\n\tSCL = 0;//拉低时钟总线,允许从机控制SDA\n\tSCL = 1;//拉高,读SDA\n\tdelay5us();\n\tif(SDA)//NOACK\n\t{\n\t\tSCL = 0;\n\t\treturn(1);//返回1\n\t}\n\telse//ACK\n\t{\n\t\tSCL = 0;\n\t\treturn(0);//返回0\n\t}\n}\n\n/*====================================\n函数\t:SendACK(bit i)\n参数\t:1主机发送非应答,0发送应答\n返回值\t:无\n描述\t:主机发送应答信号\n====================================*/\nvoid SendACK(bit i)\n{\n\tSCL = 0;//拉低时钟总线,允许主机控制SDA\n\tif(i)\t//发非应答\n\t\tSDA = 1;\n\telse\t//发应答\n\t\tSDA = 0;\n\tSCL = 1; //拉高总线,让从机读SDA\n\tdelay5us();//保持5us\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:I2cSendByte(uchar DAT)\n参数\t:DAT需要发送的数据\n返回值\t:无\n描述\t:I2C发送一个字节数据\n====================================*/\nvoid I2cSendByte(uchar DAT)\n{\n\tuchar i; \n\tfor(i=0; i<8; i++) //分别写8次,每次写1位\n\t{\n\t\tSCL = 0;//拉低时钟总线,允许SDA变化\n\t\tif(DAT & 0x80)//先写数据最高位\n\t\t\tSDA = 1; //写1\n\t\telse\n\t\t\tSDA = 0; //写0\n\t\tSCL = 1;\t //拉高时钟,让从机读SDA\n\t\tDAT <<= 1;\t //为发送下一位左移1位\n\t}\n\tSCL = 0; //拉低时钟总线,允许SDA释放\n\tSDA = 1;//释放数据总线\n}\n\n/*====================================\n函数\t:Pcf8591DA(uchar Ctrl, DAT)\n参数\t:Ctrl 8591控制字节,DAT 要写入的数据\n返回值\t:无\n描述\t:PCF8591数字量转模拟量输出\n====================================*/\nvoid Pcf8591DA(uchar Ctrl, DAT)\n{\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK()) //读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(DAT);//发送一字节数据\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cStop();\t//I2C停止信号\n}\n\n\n/*====================================\n函数\t:I2cReadByte()\n参数\t:无\n返回值\t:返回读出的一字节数据\n描述\t:I2C总线读一字节数据\n====================================*/\nuchar I2cReadByte()\n{\n\tuchar i, DAT;\n\tfor(i=0; i<8; i++)//分别读8次,每次读一位\n\t{\n\t\tDAT <<= 1; //数据左移1位,准备接收一位\n\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化\n\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据\n\t\tif(SDA)\n\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0\n\t}\n\treturn(DAT); //返回读出的数据\n}\n\n/*====================================\n函数\t:PCF8591Read(uchar Ctrl)\n参数\t:Ctrl 8591控制字节\n返回值\t:AD转出的数字量\n描述\t:读指定通道的输入的模拟量专为数字量\n====================================*/\nuchar PCF8591Read(uchar Ctrl)\n{\n\tuchar DAT;\n\tI2cStart();//I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tI2cSendByte(Ctrl);//发送控制字节\n\tReadACK();//读从机应答\n\tI2cStart();//再次产生I2C起始信号\n\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读\n\tif(ReadACK())//读从机应答\n\t\tAckFlag = 1;\t//NOACK\n\telse\n\t\tAckFlag = 0;\t//ACK\n\tDAT = I2cReadByte();//读一字节\n\tSendACK(1);//主机发送非应答\n\tI2cStop(); //I2C停止信号\n\treturn(DAT);//返回读出数据\n\t\t\t\n}\n\nvoid main()//main函数自身会循环\n{\t\n\ttimer0Init();//定时器0初始化\n\twhile(1)\n\t{\n\t\tEA = 0;//屏蔽中断\n\t\tnum = PCF8591Read(CH0);//读AD通道0,光敏值\n\t\tPcf8591DA(DAout, ~num);//把光敏转出的数字量取反,输出模拟量控制LED10\n\t\tEA = 1;//开中断\n\t\tdelay(5);\n\t}\n} \n\n//定时器0中断函数\nvoid timer0() interrupt 1\n{\n\tTH0 = 0xED;\n\tTL0 = 0xFF; //定时5ms\n\tdisplay(num); //数码管显示函数\t\n} \n```\n\n","slug":"51单片机/51单片机学习笔记(五)","published":1,"updated":"2021-09-10T12:45:42.618Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6tt00h0akvehg5w1fi3","content":"

逐次逼近式AD转换器

这是一个基本原理图

\n

首先,内部产生一个参考电压Vref,然后与要进行测量的电压Vin进行比较,如果Vref<=Vin,就产生一个“1”信号(否则产生“0”信号),并存储到N位寄存器的最高位,然重新生成参考电压信号Vref = Vref+0.5Vref,(如果前面产生的是“0”信号,则新的参考电压Vref = 0.5Vref),再与Vin比较,将比较结果(“0”或“1”存到寄存器的下一位),循环往复,直到N位寄存器全部存满数据,最后通过锁存器输出转化后的数字信号。

\n

\"image-20210824143628336\"

\n

比如:Vin = 3.75V, Vref = 2.5V

\n
    \n
  1. Vref=2.5 < 3.75,因此寄存器最高位存储数据1,然后重新生成Vref = 2.5+0.5*2.5 = 3.75
  2. \n
  3. 此时Vref=3.75 == Vin, 返回比较结果1, 存储下来, 然后重新生成Vref = 3.75+3.75*0.5 = 5.6
  4. \n
  5. 此时Vref=5.6 > Vin,返回比较结果0,存储下来。。。。。
  6. \n
  7. 最后结果是:1100 0000
  8. \n
\n

PCF8591模块

简介

PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入、一个输出和一个串行 I2C 总线接口;

\n

3个地址引脚A0、A1 和 A2 用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件;

\n

PCF8591由于其使用的简单方便和集成度高,在单片机应用系统中得到了广泛的应用。

\n

特点:

\n
    \n
  1. 单电源供电

    \n
  2. \n
  3. 工作电压:2.5 V ~ 6 V

    \n
  4. \n
  5. I2C总线串行输入/输出

    \n
  6. \n
  7. 通过3个硬件地址引脚编址

    \n
  8. \n
  9. 采样速率取决于 I2C 总线传输速率决定

    \n
  10. \n
  11. 4个模拟输入可编程为单端或差分输入

    \n
  12. \n
  13. 自动增量通道选择

    \n
  14. \n
  15. 8位逐次比较型 A/D 转换

    \n
  16. \n
\n

管脚定义:

\n

\"image-20210824145451094\"

\n
    \n
  1. AIN0~AIN3:模拟量输入通道
  2. \n
  3. AOUT:模拟输出通道
  4. \n
  5. A0~A2:硬件设备地址
  6. \n
  7. VDD:电源正极
  8. \n
  9. VSS:电源负极
  10. \n
  11. VREF:参考电压输入。
  12. \n
  13. EXT:振荡器输入时,内部/外部的切换开关。
  14. \n
  15. OSC:振荡器输入/输出。
  16. \n
  17. SCL:I2C BUS时钟输入。
  18. \n
  19. SDA:I2C BUS 数据输入/输出。
  20. \n
  21. AGND:模拟地,模拟信号和基准电源的参考地
  22. \n
\n

开发板中的PCF8591接线:

\n

\"image-20210824145815414\"

\n

PCF8591地址

I2C 总线系统中的每一片PCF8591 通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分

\n

可编程部分必须根据地址引脚A0、A1 和 A2 来设置,因此I2C系统中最多可接8个PCF8591

\n

在I2C 总线协议中地址必须是起始条件后作为第一个字节发送。

\n

地址字节的最后一位是用于设置以后数据传输方向的读/写位(1为读操作,0为写操作)

\n

\"image-20210824150249772\"

\n

PCF8591控制字节

发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。

\n

控制寄存器的高半字节用于允许模拟输出,以及可以将模拟输入编程为单端或差分输入。

\n

低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置1,每次A/D 转换后通道号将自动增加。

\n

例子

(一)

利用I2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,通过数码管显示

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*********************************************************************************
* 【作 者】:\t清翔电子:向量
* 【版 本】:\tV1.0
* 【网 站】:\thttp://www.qxmcu.com/
* 【淘宝店铺】:\thttp://qxmcu.taobao.com/
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,数码管
\t\t\t\t\t显示。\t\t \t\t\t \t\t\t
* 【使用说明】: \t用手转动AD旁边的电位器头,数码管数值会随之变化。
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define PCF8591ADDR 0X90 //PCF8591地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位
#define\tCH0 0\t\t\t //AD通道0
#define\tCH1\t1\t\t\t //AD通道1
#define\tCH2\t2\t\t\t //AD通道2
#define\tCH3\t3\t\t\t //AD通道3
#define\tDAout\t0x40\t //DA输出命令

#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
使用typedef给已有数据类型取别名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;
typedef unsigned char u8;

typedef unsigned int INT16U;
typedef unsigned int uint;
typedef unsigned int u16;


sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1; //I2C时钟总线
sbit SDA = P2^0; //I2C数据总线
uint num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

//数码管位选码 //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数名\t:void delay(INT16U ms)
参数\t:ms,毫秒延时形参
返回值\t:无
描述\t:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void delay(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}
/*====================================
函数\t:void delay5us()
参数\t:无
返回值\t:无
描述\t:12T 51单片机5微秒延时函数自适应时钟
====================================*/
void delay5us()
{
\t#if MAIN_Fosc == 11059200
\t\t_nop_();
\t#elif MAIN_Fosc == 12000000
\t\t_nop_();
\t#elif MAIN_Fosc == 22118400
\t\t_nop_(); _nop_(); _nop_();
\t#endif
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示变量取值0-65535
返回值\t:无
描述\t:数码管动态显示函数第一位显示小数点
====================================*/
//void display(uint i)
//{
//\tstatic uchar wei;
//\t \t\t
//\tP0 = 0XFF;//清除断码
//\tWE = 1;//打开位选锁存器
//\tP0 = SMGwei[wei];
//\tWE = 0;//锁存位选数据
//
//\tswitch(wei)
//\t{
//\t\tcase 0: DU = 1; P0 = SMGduan[i / 1000] | 0x80; \tDU = 0; break;//千位显示小数点
//\t\tcase 1: DU = 1; P0 = SMGduan[i % 1000 / 100]; \tDU = 0; break;//显示百位
//\t\tcase 2: DU = 1; P0 = SMGduan[i % 100 / 10]; \tDU = 0; break;//显示十位\t
//\t\tcase 3: DU = 1; P0 = SMGduan[i % 10]; \t\t\tDU = 0; break;//显示个位\t\t
//\t}
//\twei++;
//\tif(wei == 4)
//\t\twei = 0;
//}

/*====================================
函数:display(INT16U Value)
参数:Value,显示值 取值0-65535
描述:共阴极数码管显示函数可显示一个字节的数
====================================*/
void display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型
{\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t//关闭段选
\tP0 = SMGduan[Value/1000]|0x80;\t//数码管显示千位
\tDU = 1;\t\t\t\t\t\t\t//打开段选
\tDU = 0;\t\t\t\t\t\t\t//关闭段选

\tWE = 0;\t\t\t\t\t\t//关闭位选
\tP0 = SMGwei[0];\t\t\t\t //第一位数码管
\tWE = 1;\t\t\t\t\t\t//打开位选
\tWE = 0;\t\t\t\t\t\t//关闭位选
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%1000/100]; //显示百位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[1];\t\t\t //第二位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%100/10];\t\t//显示十位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[2];\t\t\t\t//第三位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%10];\t\t//显示个位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[3];\t\t\t\t//第四位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
}

/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart()
{
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop()
{
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK()
{
\tSCL = 0;//拉低时钟总线,允许从机控制SDA
\tSCL = 1;//拉高,读SDA
\tdelay5us();
\tif(SDA)//NOACK
\t{
\t\tSCL = 0;
\t\treturn(1);//返回1
\t}
\telse//ACK
\t{
\t\tSCL = 0;
\t\treturn(0);//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i)
{
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT)
{
\tuchar i;
\tfor(i=0; i<8; i++) //分别写8次,每次写1位
\t{
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:Pcf8591DA(uchar Ctrl, DAT)
参数\t:Ctrl 8591控制字节,DAT 要写入的数据
返回值\t:无
描述\t:PCF8591数字量转模拟量输出
====================================*/
//void Pcf8591DA(uchar Ctrl, DAT)
//{
//\tI2cStart();//I2C起始信号
//\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
//\tif(ReadACK()) //读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cSendByte(Ctrl);//发送控制字节
//\tif(ReadACK())//读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cSendByte(DAT);//发送一字节数据
//\tif(ReadACK())//读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cStop();\t//I2C停止信号
//}


/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte()
{
\tuchar i, DAT;
\tfor(i=0; i<8; i++)//分别读8次,每次读一位
\t{
\t\tDAT <<= 1; //数据左移1位,准备接收一位
\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); //返回读出的数据
}

/*====================================
函数\t:PCF8591Read(uchar Ctrl)
参数\t:Ctrl 8591控制字节
返回值\t:AD转出的数字量
描述\t:读指定通道的输入的模拟量专为数字量
====================================*/
uchar PCF8591Read(uchar Ctrl)
{
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环
{\t
\twhile(1)
\t{
\t\tnum = (PCF8591Read(CH2) * 19.53 + 0.5);//读AD通道2,电位器值
\t\t// 5000mv/256约等于等于19.53 ,加0.5是小数四舍五入接近实际转换精度
\t\tdisplay(num); //数码管显示函数
\t\tdelay(5);
\t}
}

\n

(二)

使用I2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10亮度变化

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/*********************************************************************************
* 【作 者】:\t清翔电子:向量
* 【版 本】:\tV1.0
* 【网 站】:\thttp://www.qxmcu.com/
* 【淘宝店铺】:\thttp://qxmcu.taobao.com/
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10\t\t \t\t\t \t\t\t
* 【使用说明】: \t随着环境光线的变强LED10亮度增加,环境光变弱LED10亮度变暗
\t\t\t\t 需要用跳线帽把DEN和DOUT短接(默认已短接)
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char
#define PCF8591ADDR 0X90 //PCF8591硬件地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位
#define\tCH0 0\t\t\t //AD通道0
#define\tCH1\t1\t\t\t //AD通道1
#define\tCH2\t2\t\t\t //AD通道2
#define\tCH3\t3\t\t\t //AD通道3
#define\tDAout\t0x40\t //DA输出命令

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1; //I2C时钟总线
sbit SDA = P2^0; //I2C数据总线
uchar num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

/*====================================
函数\t: delay(uint z)
参数\t:z 延时毫秒设定,取值范围0-65535
返回值\t:无
描述\t:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示数值,取值范围0-255
返回值\t:无
描述\t:三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void timer0Init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}
/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:delay5us()
参数\t:无
返回值\t:无
描述\t:5us延时函数
====================================*/
void delay5us()
{
\t_nop_();
}

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart()
{
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop()
{
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK()
{
\tSCL = 0;//拉低时钟总线,允许从机控制SDA
\tSCL = 1;//拉高,读SDA
\tdelay5us();
\tif(SDA)//NOACK
\t{
\t\tSCL = 0;
\t\treturn(1);//返回1
\t}
\telse//ACK
\t{
\t\tSCL = 0;
\t\treturn(0);//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i)
{
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT)
{
\tuchar i;
\tfor(i=0; i<8; i++) //分别写8次,每次写1位
\t{
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:Pcf8591DA(uchar Ctrl, DAT)
参数\t:Ctrl 8591控制字节,DAT 要写入的数据
返回值\t:无
描述\t:PCF8591数字量转模拟量输出
====================================*/
void Pcf8591DA(uchar Ctrl, DAT)
{
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK()) //读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(DAT);//发送一字节数据
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cStop();\t//I2C停止信号
}


/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte()
{
\tuchar i, DAT;
\tfor(i=0; i<8; i++)//分别读8次,每次读一位
\t{
\t\tDAT <<= 1; //数据左移1位,准备接收一位
\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); //返回读出的数据
}

/*====================================
函数\t:PCF8591Read(uchar Ctrl)
参数\t:Ctrl 8591控制字节
返回值\t:AD转出的数字量
描述\t:读指定通道的输入的模拟量专为数字量
====================================*/
uchar PCF8591Read(uchar Ctrl)
{
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环
{\t
\ttimer0Init();//定时器0初始化
\twhile(1)
\t{
\t\tEA = 0;//屏蔽中断
\t\tnum = PCF8591Read(CH0);//读AD通道0,光敏值
\t\tPcf8591DA(DAout, ~num);//把光敏转出的数字量取反,输出模拟量控制LED10
\t\tEA = 1;//开中断
\t\tdelay(5);
\t}
}

//定时器0中断函数
void timer0() interrupt 1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

逐次逼近式AD转换器

这是一个基本原理图

\n

首先,内部产生一个参考电压Vref,然后与要进行测量的电压Vin进行比较,如果Vref<=Vin,就产生一个“1”信号(否则产生“0”信号),并存储到N位寄存器的最高位,然重新生成参考电压信号Vref = Vref+0.5Vref,(如果前面产生的是“0”信号,则新的参考电压Vref = 0.5Vref),再与Vin比较,将比较结果(“0”或“1”存到寄存器的下一位),循环往复,直到N位寄存器全部存满数据,最后通过锁存器输出转化后的数字信号。

\n

\"image-20210824143628336\"

\n

比如:Vin = 3.75V, Vref = 2.5V

\n
    \n
  1. Vref=2.5 < 3.75,因此寄存器最高位存储数据1,然后重新生成Vref = 2.5+0.5*2.5 = 3.75
  2. \n
  3. 此时Vref=3.75 == Vin, 返回比较结果1, 存储下来, 然后重新生成Vref = 3.75+3.75*0.5 = 5.6
  4. \n
  5. 此时Vref=5.6 > Vin,返回比较结果0,存储下来。。。。。
  6. \n
  7. 最后结果是:1100 0000
  8. \n
\n

PCF8591模块

简介

PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入、一个输出和一个串行 I2C 总线接口;

\n

3个地址引脚A0、A1 和 A2 用于编程硬件地址,允许将最多8个器件连接至I2C总线而不需要额外硬件;

\n

PCF8591由于其使用的简单方便和集成度高,在单片机应用系统中得到了广泛的应用。

\n

特点:

\n
    \n
  1. 单电源供电

    \n
  2. \n
  3. 工作电压:2.5 V ~ 6 V

    \n
  4. \n
  5. I2C总线串行输入/输出

    \n
  6. \n
  7. 通过3个硬件地址引脚编址

    \n
  8. \n
  9. 采样速率取决于 I2C 总线传输速率决定

    \n
  10. \n
  11. 4个模拟输入可编程为单端或差分输入

    \n
  12. \n
  13. 自动增量通道选择

    \n
  14. \n
  15. 8位逐次比较型 A/D 转换

    \n
  16. \n
\n

管脚定义:

\n

\"image-20210824145451094\"

\n
    \n
  1. AIN0~AIN3:模拟量输入通道
  2. \n
  3. AOUT:模拟输出通道
  4. \n
  5. A0~A2:硬件设备地址
  6. \n
  7. VDD:电源正极
  8. \n
  9. VSS:电源负极
  10. \n
  11. VREF:参考电压输入。
  12. \n
  13. EXT:振荡器输入时,内部/外部的切换开关。
  14. \n
  15. OSC:振荡器输入/输出。
  16. \n
  17. SCL:I2C BUS时钟输入。
  18. \n
  19. SDA:I2C BUS 数据输入/输出。
  20. \n
  21. AGND:模拟地,模拟信号和基准电源的参考地
  22. \n
\n

开发板中的PCF8591接线:

\n

\"image-20210824145815414\"

\n

PCF8591地址

I2C 总线系统中的每一片PCF8591 通过发送有效地址到该器件来激活。该地址包括固定部分和可编程部分

\n

可编程部分必须根据地址引脚A0、A1 和 A2 来设置,因此I2C系统中最多可接8个PCF8591

\n

在I2C 总线协议中地址必须是起始条件后作为第一个字节发送。

\n

地址字节的最后一位是用于设置以后数据传输方向的读/写位(1为读操作,0为写操作)

\n

\"image-20210824150249772\"

\n

PCF8591控制字节

发送到 PCF8591 的第二个字节将被存储在控制寄存器,用于控制器件功能。

\n

控制寄存器的高半字节用于允许模拟输出,以及可以将模拟输入编程为单端或差分输入。

\n

低半字节选择一个由高半字节定义的模拟输入通道。如果自动增量(auto-increment)标志置1,每次A/D 转换后通道号将自动增加。

\n

例子

(一)

利用I2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,通过数码管显示

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*********************************************************************************
* 【作 者】:\t清翔电子:向量
* 【版 本】:\tV1.0
* 【网 站】:\thttp://www.qxmcu.com/
* 【淘宝店铺】:\thttp://qxmcu.taobao.com/
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道2电位器模拟量,数码管
\t\t\t\t\t显示。\t\t \t\t\t \t\t\t
* 【使用说明】: \t用手转动AD旁边的电位器头,数码管数值会随之变化。
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define PCF8591ADDR 0X90 //PCF8591地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位
#define\tCH0 0\t\t\t //AD通道0
#define\tCH1\t1\t\t\t //AD通道1
#define\tCH2\t2\t\t\t //AD通道2
#define\tCH3\t3\t\t\t //AD通道3
#define\tDAout\t0x40\t //DA输出命令

#define MAIN_Fosc\t\t11059200UL\t//宏定义主时钟HZ
/*====================================
使用typedef给已有数据类型取别名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;
typedef unsigned char u8;

typedef unsigned int INT16U;
typedef unsigned int uint;
typedef unsigned int u16;


sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1; //I2C时钟总线
sbit SDA = P2^0; //I2C数据总线
uint num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};

//数码管位选码 //第1位\t2位\t 3位\t 4位 5位\t6位\t 7位\t8位
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码

/*====================================
函数名\t:void delay(INT16U ms)
参数\t:ms,毫秒延时形参
返回值\t:无
描述\t:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void delay(INT16U ms)
{
INT16U i;
\t do{
\t i = MAIN_Fosc / 96000;
\t\t while(--i); //96T per loop
}while(--ms);
}
/*====================================
函数\t:void delay5us()
参数\t:无
返回值\t:无
描述\t:12T 51单片机5微秒延时函数自适应时钟
====================================*/
void delay5us()
{
\t#if MAIN_Fosc == 11059200
\t\t_nop_();
\t#elif MAIN_Fosc == 12000000
\t\t_nop_();
\t#elif MAIN_Fosc == 22118400
\t\t_nop_(); _nop_(); _nop_();
\t#endif
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示变量取值0-65535
返回值\t:无
描述\t:数码管动态显示函数第一位显示小数点
====================================*/
//void display(uint i)
//{
//\tstatic uchar wei;
//\t \t\t
//\tP0 = 0XFF;//清除断码
//\tWE = 1;//打开位选锁存器
//\tP0 = SMGwei[wei];
//\tWE = 0;//锁存位选数据
//
//\tswitch(wei)
//\t{
//\t\tcase 0: DU = 1; P0 = SMGduan[i / 1000] | 0x80; \tDU = 0; break;//千位显示小数点
//\t\tcase 1: DU = 1; P0 = SMGduan[i % 1000 / 100]; \tDU = 0; break;//显示百位
//\t\tcase 2: DU = 1; P0 = SMGduan[i % 100 / 10]; \tDU = 0; break;//显示十位\t
//\t\tcase 3: DU = 1; P0 = SMGduan[i % 10]; \t\t\tDU = 0; break;//显示个位\t\t
//\t}
//\twei++;
//\tif(wei == 4)
//\t\twei = 0;
//}

/*====================================
函数:display(INT16U Value)
参数:Value,显示值 取值0-65535
描述:共阴极数码管显示函数可显示一个字节的数
====================================*/
void display(INT16U Value)\t\t\t//注意由于需要显示的数大于一个字节所有形参需为int型
{\t
//------------------------------
\tDU = 0;\t\t\t\t\t\t\t//关闭段选
\tP0 = SMGduan[Value/1000]|0x80;\t//数码管显示千位
\tDU = 1;\t\t\t\t\t\t\t//打开段选
\tDU = 0;\t\t\t\t\t\t\t//关闭段选

\tWE = 0;\t\t\t\t\t\t//关闭位选
\tP0 = SMGwei[0];\t\t\t\t //第一位数码管
\tWE = 1;\t\t\t\t\t\t//打开位选
\tWE = 0;\t\t\t\t\t\t//关闭位选
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%1000/100]; //显示百位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[1];\t\t\t //第二位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%100/10];\t\t//显示十位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[2];\t\t\t\t//第三位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
//-------------------------------
\tDU = 0;
\tP0 = SMGduan[Value%10];\t\t//显示个位
\tDU = 1;
\tDU = 0;

\tWE = 0;
\tP0 = SMGwei[3];\t\t\t\t//第四位数码管
\tWE = 1;
\tWE = 0;
\tdelay(3);
}

/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart()
{
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop()
{
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK()
{
\tSCL = 0;//拉低时钟总线,允许从机控制SDA
\tSCL = 1;//拉高,读SDA
\tdelay5us();
\tif(SDA)//NOACK
\t{
\t\tSCL = 0;
\t\treturn(1);//返回1
\t}
\telse//ACK
\t{
\t\tSCL = 0;
\t\treturn(0);//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i)
{
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT)
{
\tuchar i;
\tfor(i=0; i<8; i++) //分别写8次,每次写1位
\t{
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:Pcf8591DA(uchar Ctrl, DAT)
参数\t:Ctrl 8591控制字节,DAT 要写入的数据
返回值\t:无
描述\t:PCF8591数字量转模拟量输出
====================================*/
//void Pcf8591DA(uchar Ctrl, DAT)
//{
//\tI2cStart();//I2C起始信号
//\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
//\tif(ReadACK()) //读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cSendByte(Ctrl);//发送控制字节
//\tif(ReadACK())//读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cSendByte(DAT);//发送一字节数据
//\tif(ReadACK())//读从机应答
//\t\tAckFlag = 1;\t//NOACK
//\telse
//\t\tAckFlag = 0;\t//ACK
//\tI2cStop();\t//I2C停止信号
//}


/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte()
{
\tuchar i, DAT;
\tfor(i=0; i<8; i++)//分别读8次,每次读一位
\t{
\t\tDAT <<= 1; //数据左移1位,准备接收一位
\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); //返回读出的数据
}

/*====================================
函数\t:PCF8591Read(uchar Ctrl)
参数\t:Ctrl 8591控制字节
返回值\t:AD转出的数字量
描述\t:读指定通道的输入的模拟量专为数字量
====================================*/
uchar PCF8591Read(uchar Ctrl)
{
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环
{\t
\twhile(1)
\t{
\t\tnum = (PCF8591Read(CH2) * 19.53 + 0.5);//读AD通道2,电位器值
\t\t// 5000mv/256约等于等于19.53 ,加0.5是小数四舍五入接近实际转换精度
\t\tdisplay(num); //数码管显示函数
\t\tdelay(5);
\t}
}

\n

(二)

使用I2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10亮度变化

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/*********************************************************************************
* 【作 者】:\t清翔电子:向量
* 【版 本】:\tV1.0
* 【网 站】:\thttp://www.qxmcu.com/
* 【淘宝店铺】:\thttp://qxmcu.taobao.com/
* 【实验平台】:\t清翔 QX-MCS51 单片机开发板
* 【外部晶振】: \t11.0592mhz\t
* 【主控芯片】: \tSTC89C52
* 【编译环境】: \tKeil μVisio4\t
* 【程序功能】: \tI2C通信,PCF8591模数/数模转换,读取通道0光敏模拟量,输出模拟量控制LED10\t\t \t\t\t \t\t\t
* 【使用说明】: \t随着环境光线的变强LED10亮度增加,环境光变弱LED10亮度变暗
\t\t\t\t 需要用跳线帽把DEN和DOUT短接(默认已短接)
**********************************************************************************/
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char
#define PCF8591ADDR 0X90 //PCF8591硬件地址
#define\tI2cRead 1\t\t //I2C读方向位
#define\tI2cWrite 0\t\t //I2C写方向位
#define\tCH0 0\t\t\t //AD通道0
#define\tCH1\t1\t\t\t //AD通道1
#define\tCH2\t2\t\t\t //AD通道2
#define\tCH3\t3\t\t\t //AD通道3
#define\tDAout\t0x40\t //DA输出命令

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit SCL = P2^1; //I2C时钟总线
sbit SDA = P2^0; //I2C数据总线
uchar num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

/*====================================
函数\t: delay(uint z)
参数\t:z 延时毫秒设定,取值范围0-65535
返回值\t:无
描述\t:12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
\tuint x,y;
\tfor(x = z; x > 0; x--)
\t\tfor(y = 114; y > 0 ; y--); \t\t
}

/*====================================
函数\t:display(uchar i)
参数\t:i 显示数值,取值范围0-255
返回值\t:无
描述\t:三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
\tstatic uchar wei; \t\t
\tP0 = 0XFF;//清除断码
\tWE = 1;//打开位选锁存器
\tP0 = SMGwei[wei];
\tWE = 0;//锁存位选数据
\tswitch(wei)
\t{
\t\tcase 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
\t\tcase 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;\t
\t\tcase 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;\t\t
\t}
\twei++;
\tif(wei == 3)
\t\twei = 0;
}
//定时器0初始化
void timer0Init()
{
\tEA = 1;\t//打开总中断
\tET0 = 1;//打开定时器0中断
\tTR0 = 1;\t //启动定时器0
\tTMOD |= 0X01; //定时器工作模式1,16位定时模式
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
}
/****************************************************
IIC通信代码
****************************************************/

/*====================================
函数\t:delay5us()
参数\t:无
返回值\t:无
描述\t:5us延时函数
====================================*/
void delay5us()
{
\t_nop_();
}

/*====================================
函数\t:I2cStart()
参数\t:无
返回值\t:无
描述\t:I2C总线起始信号
====================================*/
void I2cStart()
{
//时钟总线为高电平期间数据总线又高变低产生起始型号
\tSCL = 1;
\tSDA = 1;
\tdelay5us();//状态保持5us
\tSDA = 0;
\tdelay5us();//状态保持5us
}

/*====================================
函数\t:I2cStop()
参数\t:无
返回值\t:无
描述\t:I2C总线停止信号
====================================*/
void I2cStop()
{
//时钟总线为高电平期间,数据总线从高变低产生终止信号
\tSCL = 0;
\tSDA = 0;
\tSCL = 1;
\tdelay5us();//状态保持5us
\tSDA = 1;
\tdelay5us();//状态保持5us\t
}

/*====================================
函数\t:ReadACK()
参数\t:无
返回值\t:1非应答,0应答
描述\t:I2C总线读从机应答信号
====================================*/
bit ReadACK()
{
\tSCL = 0;//拉低时钟总线,允许从机控制SDA
\tSCL = 1;//拉高,读SDA
\tdelay5us();
\tif(SDA)//NOACK
\t{
\t\tSCL = 0;
\t\treturn(1);//返回1
\t}
\telse//ACK
\t{
\t\tSCL = 0;
\t\treturn(0);//返回0
\t}
}

/*====================================
函数\t:SendACK(bit i)
参数\t:1主机发送非应答,0发送应答
返回值\t:无
描述\t:主机发送应答信号
====================================*/
void SendACK(bit i)
{
\tSCL = 0;//拉低时钟总线,允许主机控制SDA
\tif(i)\t//发非应答
\t\tSDA = 1;
\telse\t//发应答
\t\tSDA = 0;
\tSCL = 1; //拉高总线,让从机读SDA
\tdelay5us();//保持5us
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:I2cSendByte(uchar DAT)
参数\t:DAT需要发送的数据
返回值\t:无
描述\t:I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT)
{
\tuchar i;
\tfor(i=0; i<8; i++) //分别写8次,每次写1位
\t{
\t\tSCL = 0;//拉低时钟总线,允许SDA变化
\t\tif(DAT & 0x80)//先写数据最高位
\t\t\tSDA = 1; //写1
\t\telse
\t\t\tSDA = 0; //写0
\t\tSCL = 1;\t //拉高时钟,让从机读SDA
\t\tDAT <<= 1;\t //为发送下一位左移1位
\t}
\tSCL = 0; //拉低时钟总线,允许SDA释放
\tSDA = 1;//释放数据总线
}

/*====================================
函数\t:Pcf8591DA(uchar Ctrl, DAT)
参数\t:Ctrl 8591控制字节,DAT 要写入的数据
返回值\t:无
描述\t:PCF8591数字量转模拟量输出
====================================*/
void Pcf8591DA(uchar Ctrl, DAT)
{
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK()) //读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(DAT);//发送一字节数据
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cStop();\t//I2C停止信号
}


/*====================================
函数\t:I2cReadByte()
参数\t:无
返回值\t:返回读出的一字节数据
描述\t:I2C总线读一字节数据
====================================*/
uchar I2cReadByte()
{
\tuchar i, DAT;
\tfor(i=0; i<8; i++)//分别读8次,每次读一位
\t{
\t\tDAT <<= 1; //数据左移1位,准备接收一位
\t\tSCL = 0; //拉低时钟总线,允许从机控制SDA变化
\t\tSCL = 1; //拉高时钟总线,读取SDA上的数据
\t\tif(SDA)
\t\t\tDAT |= 0X01;//为1则写1,否则不行执行写1,通过左移补0
\t}
\treturn(DAT); //返回读出的数据
}

/*====================================
函数\t:PCF8591Read(uchar Ctrl)
参数\t:Ctrl 8591控制字节
返回值\t:AD转出的数字量
描述\t:读指定通道的输入的模拟量专为数字量
====================================*/
uchar PCF8591Read(uchar Ctrl)
{
\tuchar DAT;
\tI2cStart();//I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cWrite);//发送器件地址加读写方向位
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tI2cSendByte(Ctrl);//发送控制字节
\tReadACK();//读从机应答
\tI2cStart();//再次产生I2C起始信号
\tI2cSendByte(PCF8591ADDR + I2cRead);//发送器件地址加读写方向位 读
\tif(ReadACK())//读从机应答
\t\tAckFlag = 1;\t//NOACK
\telse
\t\tAckFlag = 0;\t//ACK
\tDAT = I2cReadByte();//读一字节
\tSendACK(1);//主机发送非应答
\tI2cStop(); //I2C停止信号
\treturn(DAT);//返回读出数据
\t\t\t
}

void main()//main函数自身会循环
{\t
\ttimer0Init();//定时器0初始化
\twhile(1)
\t{
\t\tEA = 0;//屏蔽中断
\t\tnum = PCF8591Read(CH0);//读AD通道0,光敏值
\t\tPcf8591DA(DAout, ~num);//把光敏转出的数字量取反,输出模拟量控制LED10
\t\tEA = 1;//开中断
\t\tdelay(5);
\t}
}

//定时器0中断函数
void timer0() interrupt 1
{
\tTH0 = 0xED;
\tTL0 = 0xFF; //定时5ms
\tdisplay(num); //数码管显示函数\t
}
\n"},{"title":"butterfly主题配置记录","description":"用于记录搭建Hexo博客的过程即butterfly主题的配置","abbrlink":"dc7f8ae1","date":"2021-08-29T02:20:29.000Z","swiper_index":10,"cover":"https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png","_content":"\n\n\n## 下载安装\n\n{% folding 参考店长文章 %}\n\n\n1. [快速开始](https://butterfly.js.org/posts/21cfbf15/)\n2. [主题页面](https://butterfly.js.org/posts/dc584b87/)\n3. [主题配置1](https://butterfly.js.org/posts/4aa8abbe/)\n4. [主题配置2](https://butterfly.js.org/posts/ceeb73f/)\n5. [进阶教程](https://butterfly.js.org/posts/4073eda/)\n\n{% endfolding %}\n\n\n\n## 页面主题颜色配置\n\n{% folding 配置 %}\n\n\n在主题配置文件`_config.butterfly.yml`修改即可\n\n```yaml\ntheme_color:\n enable: true\n main: \"#49B1F5\"\n paginator: \"#00c4b6\"\n button_hover: \"#FF7242\"\n text_selection: \"#00c4b6\"\n link_color: \"#99a9bf\"\n meta_color: \"#858585\"\n hr_color: \"#A4D8FA\"\n code_foreground: \"#F47466\"\n code_background: \"rgba(27, 31, 35, .05)\"\n toc_color: \"#00c4b6\"\n blockquote_padding_color: \"#49b1f5\"\n blockquote_background_color: \"#49b1f5\"\n\n```\n\n{% endfolding %}\n\n## 外挂标签\n\n参考小康博客\n{% folding 查看%}\n[小康博客butterfly主题使用文档](https://www.antmoe.com/posts/3b43914f/)\n{% endfolding %}\n\n\n## 全局背景透明渐变\n[小康博客](https://www.antmoe.com/posts/7198453/)\n\n\n\n\n\n\n\n\n## 首页置顶轮播图\n\n[糖果屋教程](https://www.akilar.top/posts/8e1264d1/)\n\n{% folding cyan , 步骤 %}\n\n1. 安装插件\n ```\n npm install hexo-butterfly-swiper --save\n ```\n\n2. 在站点文件 `_config.yml`添加如下内容:\n ```yml\n # hexo-butterfly-swiper\n # see https://akilar.top/posts/8e1264d1/\n swiper:\n enable: true # 开关\n priority: 5 #过滤器优先权\n enable_page: all # 应用页面\n timemode: date #date/updated\n layout: # 挂载容器类型\n type: id\n name: recent-posts\n index: 0\n default_descr: 再怎么看我也不知道怎么描述它的啦!\n custom_css: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/css/swiper.min.css #自定义swiper css依赖\n custom_js: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/js/swiper.min.js #自定义swiper js依赖\n ```\n\n| 参数 | 备选值/类型 | 释义 |\n| :------------ | :----------- | :----------------------------------------------------------- |\n| priority | number | 【可选】过滤器优先级,数值越小,执行越早,默认为10,选填 |\n| enable | true/false | 【必选】控制开关 |\n| enable_page | path/all | 【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为all |\n| timemode | date/updated | 【可选】时间显示,date为显示创建日期,updated为显示更新日期,默认为date |\n| layout.type | id/class | 【可选】挂载容器类型,填写id或class,不填则默认为id |\n| layout.name | text | 【必选】挂载容器名称 |\n| layout.index | 0和正整数 | 【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位 |\n| default_descr | text | 默认文章描述 |\n| custom_css | url | 【可选】自定义的swiper依赖项css链接 |\n| custom_js | url | 【可选】自定义的swiper依赖项加js链接 |\n\n3. 在文章的 `front_matter` 添加 `swiper_index` 配置项即可\n\n ```yml\n ---\n title: 文章标题\n date: 创建日期\n updated: 更新日期\n cover: 文章封面\n description: 文章描述\n swiper_index: 1 #置顶轮播图顺序,非负整数,数字越大越靠前\n ---\n ```\n\n{% endfolding %}\n\n\n\n\n\n## 首页卡片浮动显示wowjs\n\n{% folding blue, 插件版本%}\n\n[糖果屋教程](https://akilar.top/posts/abab51cf/)\n\n1. 安装插件\n\n ```\n npm install hexo-butterfly-wowjs --save\n ```\n\n2. 添加配置信息,以下为写法示例\n 在站点配置文件`_config.yml` 或者主题配置文件`_config.butterfly.yml` 中添加\n\n ```yml\n wowjs:\n enable: true #控制动画开关。true是打开,false是关闭\n priority: 10 #过滤器优先级\n mobile: false #移动端是否启用,默认移动端禁用\n animateitem:\n - class: recent-post-item #必填项,需要添加动画的元素的class\n style: animate__zoomIn #必填项,需要添加的动画\n duration: 2s #选填项,动画持续时间,单位可以是ms也可以是s。例如3s,700ms。\n delay: 1s #选填项,动画开始的延迟时间,单位可以是ms也可以是s。例如3s,700ms。\n offset: 100 #选填项,开始动画的距离(相对浏览器底部)\n iteration: 2 #选填项,动画重复的次数\n - class: card-widget\n style: animate__zoomIn\n ```\n\n\n\n| 参数 | 备选值 / 类型 | 释义 |\n| :-------------------- | :--------------------- | :----------------------------------------------------------- |\n| enable | true/false | 【必选】控制开关 |\n| priority | number | 【可选】过滤器优先级,数值越小,执行越早,默认为 10,选填 |\n| mobile | true/false | 控制移动端是否启用,默认移动端禁用 |\n| animateitem.class | class | 【可选】添加动画类名,只支持给 class 添加 |\n| animateitem.style | text | 【可选】动画样式,具体类型参考 [animate.css](https://animate.style/) |\n| animateitem.duration | time, 单位为 s 或 ms | 【可选】动画持续时间,单位可以是 ms 也可以是 s。例如 3s,700ms。 |\n| animateitem.delay | time, 单位为 s 或 ms | 【可选】动画开始的延迟时间,单位可以是 ms 也可以是 s。例如 3s,700ms。 |\n| animateitem.offset | number, 单位为 px | 【可选】开始动画的距离(相对浏览器底部)。 |\n| animateitem.iteration | number, 单位为 s 或 ms | 【可选】动画重复的次数 |\n\n\n\n{%endfolding%}\n\n\n## 看板娘配置\n\n[糖果屋教程](https://akilar.top/posts/5b8f515f/)\n\n\n看板娘不能换装,参考糖果屋的解决方法:\n\n{%folding blue, 点击展开%}\n使用糖果屋大佬配置好的本地化项目的路径:\n修改项目内的 `~\\live2d-widget\\autoload.js`, 修改 `cdnPath`\n\n```diff\n // 加载 waifu.css live2d.min.js waifu-tips.js\n if (screen.width >= 768) {\n \tPromise.all([\n \t\tloadExternalResource(live2d_path + \"waifu.css\", \"css\"),\n \t\tloadExternalResource(live2d_path + \"live2d.min.js\", \"js\"),\n \t\tloadExternalResource(live2d_path + \"waifu-tips.js\", \"js\")\n \t]).then(() => {\n \t\tinitWidget({\n \t\t\twaifuPath: live2d_path + \"waifu-tips.json\",\n \t\t\t//apiPath: \"https://live2d.fghrsh.net/api/\",\n- \t\t\tcdnPath: \"https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/\"\n+ \t\t\tcdnPath: \"https://live2d-api.vercel.app/\"\n//因为jsdelivr不支持50MB以上的包的加速,可能报403错误,所以用的vercel的CDN服务。\n//可以考虑clone大佬配置好的live2d_api仓库自己部署到其他更快的cdn服务上。\n \t\t});\n \t});\n }\n\n```\n\n{%endfolding%}\n\n\n## 主页分类磁铁配置\n\n[糖果屋教程](https://akilar.top/posts/a9131002/)\n\n{%folding blue, 点击展开%}\n\n(1)修改`[Blogroot]\\themes\\butterfly\\layout\\index.pug`, 添加6~8行代码,以下是添加后的结果(注意代码格式比较严格,可直接复制)\n\n```\nextends includes/layout.pug\n\nblock content\n include ./includes/mixins/post-ui.pug\n #recent-posts.recent-posts\n if theme.categoryBar.enable\n .recent-post-item(style='height:auto;width:100%;padding:0px;')\n #categoryBar!= list_categories(site.categories,{class: 'categoryBar',depth: 1})\n +postUI\n include includes/pagination.pug\n\n```\n\n(2)新建`[Blogroot]\\themes\\butterfly\\source\\css\\_layout\\categoryBar.styl`\n\n```\nif hexo-config('categoryBar.enable')\n #categoryBar\n width 100%!important\n ul\n &.categoryBar-list\n margin 5px 5px 0 5px!important\n padding 0!important\n\n li\n &.categoryBar-list-item\n font-weight bold\n display inline-block\n height 180px!important\n margin 5px .5% 0 .5%!important\n background-image linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(16, 16, 16, 0) 100%)\n border-radius 10px\n padding 25px 0 25px 25px!important\n box-shadow rgba(50, 50, 50, 0.3) 50px 50px 50px 50px inset\n overflow hidden\n background-size 100%!important\n background-position center!important\n &:hover\n background-size 110%!important\n box-shadow inset 500px 50px 50px 50px rgba(50,50,50, 0.6)\n span\n &.categoryBar-list-count\n &::after\n transition all .5s\n transform translate(-100%, 0)\n a\n &.categoryBar-list-link\n color white!important\n font-size 20px!important\n &::before\n content '|'!important\n color white!important\n font-size 20px!important\n &:after\n content ''\n position relative\n width 0\n bottom 0\n display block\n height 3px\n border-radius 3px\n background-color white\n &:hover\n &:after\n width 90%\n left 1%\n transition all 0.5s\n\n span\n &.categoryBar-list-count\n display block!important\n color white!important\n font-size 20px!important\n &::before\n content '\\f02d'!important\n padding-right 15px!important\n @extend .fontawesomeIcon\n &::after\n padding 5px\n display block!important\n color white!important\n font-size 20px!important\n position relative\n right -100%\n covers = hexo-config('categoryBar.cover')\n for cover,i in covers\n li.categoryBar-list-item:nth-child({i+1})\n background unquote(cover)\n descrs = hexo-config('categoryBar.descr')\n for descr,i in descrs\n li.categoryBar-list-item:nth-child({i+1})>span::after\n content descr!important\n if hexo-config('categoryBar.column') == 'odd'\n li\n &.categoryBar-list-item\n width 32.3%!important\n else if hexo-config('categoryBar.column') == 'even'\n li\n &.categoryBar-list-item\n width 24%!important\n @media screen and (max-width: 650px)\n li\n &.categoryBar-list-item\n width 48%!important\n height 150px!important\n margin 5px 1% 0 1%!important\n\n $caterow = hexo-config('categoryBar.row')?hexo-config('categoryBar.row'):2\n .categoryBar-list\n max-height 190px * $caterow\n overflow auto\n &::-webkit-scrollbar\n width 0!important\n @media screen and (max-width: 650px)\n .categoryBar-list\n max-height 160px * $caterow\n```\n\n(3)在`[Blogroot]\\_config.butterfly.yml`添加配置项:\n\n```yml\ncategoryBar:\n enable: true\n column: odd # 显示列数,odd:3列 | even:4列\n row: 1 #显示行数,默认两行,超过行数切换为滚动显示\n descr:\n - Hexo博客搭建记录\n - Java学习笔记\n - 电磁场课程学习笔记\n - 硬件学习过程记录\n cover:\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover1.webp')\n - '#abcdef' # HEX格式色值需要用''包裹,不然会被识别成注释\n - rgba(45,67,89,0.7)\n - linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(200,16 , 16, 0) 100%)\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover5.webp')\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover6.webp')\n\n```\n\n{%endfolding%}\n\n\n\n\n## 主页添加文章分类配置(与上一个样式不同)\n\n{%folding blue, 展开%}\n\n[前往小冰博客](https://zfe.space/post/hexo-magnet.html)\n\n效果如下:\n![磁体2.gif](https://cdn.nlark.com/yuque/0/2021/gif/8391485/1615908841586-e6737009-b2dd-4004-ae02-4c6d15f33701.gif)\n这个插件主要实现了以下功能:\n1.自定义 tags 或 categories 的排列和展示 \n2.自定义 tags 或 categories 的展示图标,名称 \n3.自定义排列的行数,默认 2 行\n\n**NPM 插件安装的部署方法**\n\n```powershell\nnpm i hexo-magnet --save\n\n# 或者\n\ncnpm i hexo-magnet --save\n```\n\n注意,一定要加`--save`,不然本地预览的时候可能不会显示!!!\n\n**新增网站根目录\\_config 配置项(不是主题的)**\n\n```yaml\nmagnet:\n enable: true\n priority: 1\n enable_page: /\n type: categories\n devide: 2\n display:\n - name: java\n display_name: aJreamのJava全栈学习\n icon: 📚\n - name: Hexo\n display_name: aJreamのHexo博客搭建&主题配置\n icon: 🎮\n - name: win10\n display_name: aJreamのWin10主题配置\n icon: 💻\n - name: 硬件学习\n display_name: aJreamの硬件学习\n icon: 🔧\n color_setting:\n text_color: black\n text_hover_color: white\n background_color: \"#f2f2f2\"\n background_hover_color: \"#aebfe3\"\n layout:\n type: id\n name: recent-posts\n index: 0\n temple_html: '
${temple_html_item}
'\n plus_style: \n```\n\n接下来来简单说明一下配置项的含义:\n\n> **enable_page**\n\n参数:/\n含义:路由地址,如 / 代表主页。/me/代表自我介绍页等等\n\n> **priority**\n\n参数:1\n含义:插件的叠放顺序,数字越大,叠放约靠前。如果你安装了 hexo-githubcalendar,请将`hexo-githubcalendar`npm 插件更新至`@1.2.3`版本。然后给 hexo-githubcalendar 添加`priority`参数。\n\n```yaml\ngithubcalendar:\n enable: true\n priority: 3 # 这里加上参数\n```\n\n> **type**\n\n参数:categories、tags\n含义:选择筛选分类还是标签\n\n> **devide**\n\n参数:2\n含义:表示分隔的列数,2 表示分为两列展示\n\n> **display**\n\n参数:\n\n```yaml\n- name: 教程 # 这里是tags或者categories的名称\n display_name: 小冰の魔改教程 # 这里是替换的名称\n icon: 📚 # 这里是展示的图标\n```\n\n含义:配置项,可自行设置,按照设置的顺序展示\n\n> **color_setting**\n\n参数:\n\n```yaml\ntext_color: black # 文字默认颜色\ntext_hover_color: white # 文字鼠标悬浮颜色\nbackground_color: \"#f2f2f2\" # 文字背景默认颜色\nbackground_hover_color: \"#b30070\" # 文字背景悬浮颜色\n```\n\n含义:颜色配置项,可自行设置\n\n> **layout**\n\n参数:type; (class&id)\n参数:name;\n参数:index;(数字)\n含义:如果说 gihubcalendar 是一幅画,那么这个 layout 就是指定了哪面墙来挂画\n而在 HTML 的是世界里有两种墙分别 type 为 id 和 class。\n其中在定义 class 的时候会出现多个 class 的情况,这时就需要使用 index,确定是哪一个。\n最后墙的名字即是 name;\n\n```html\n
\n \n
\n
\n \n
\n
\n
\n```\n\n> **temple_html**\n\n参数:html 模板字段\n含义:包含挂载容器\n\n```xml\n
\n
\n ${temple_html_item}\n
\n
\n```\n\n> **plus_style**\n\n参数:\"\"\n含义:提供可自定义的 style,如加入黑夜模式,也可以通过CSS来添加。\n{%endfolding%}\n\n### 添加黑夜模式\n\n在 `custom.css` 文件中添加以下代码,使其在黑夜模式下生效\n```css\n[data-theme=\"dark\"] .magnet_link_context {\n background: #201e1e;\n border-radius: 6px;\n color: white;\n \n}\n\n[data-theme=\"dark\"] .magnet_link_context:hover {\n background: #3b4042;\n}\n```\n修改各类的跳转链接:\n原本点击各个分类后,默认跳转的链接是 `_config.yml` 文件中配置的 url值 + \"/categories\" + 【各个分类】\n\n但为了能使部署在不同服务器上能够跳转到自己的分类下,修改了插件`/node_modules/hexo-magnet/index.js`的代码:\n将第61行(或者在附近某行)的代码 `href=\"${hexo.config.url}/${item.path}\"` 的 `${hexo.config.url}`删掉 ,修改后如下:\n\n```js\ntemple_html_item += ``;\n```\n\n\n### hexo 三连\n\n执行 hexo 三连\n\n```powershell\nhexo clean && hexo g && hexo s\n```\n\n即可发现已经成功部署。\n\n\n\n\n## 标题h1~h6美化\n\n[参考大佬](https://guole.fun/posts/butterfly-custom/#%E6%96%87%E7%AB%A0%E9%A1%B5H1-H6%E6%A0%B7%E5%BC%8F%E4%BF%AE%E6%94%B9)\n\n{%folding blue, 展开%}\nButterfly 在 H1~H6 样式上使用了 [fontawesome.com](https://fontawesome.com/v5.15/icons?from=io)上的图标,引用的是 Unicode 形式。可自行查找合适的。\n![image-20210908221739125](https://gitee.com/ajream/images/raw/master/img/20210908221743_image-20210908221739125.png)\n\n{%tabs 小风车, -1%}\n\n本站小风车样式\n\n```yml\nbeautify:\n enable: true\n field: post # site/post\n # title-prefix-icon: '\\f0c1' 原内容\n title-prefix-icon: '\\f185'\n title-prefix-icon-color: '#F47466'\n```\n\n\n\n\n\n让小风车转起来\n\n在 `/css/custom.css` 文件中,加入以下代码即可。\n\n转速、转向修改看注释\n\n\n```css\n/* 文章页H1-H6图标样式效果 */\nh1::before,\nh2::before,\nh3::before,\nh4::before,\nh5::before,\nh6::before {\n -webkit-animation: ccc 1.6s linear infinite;\n animation: ccc 1.6s linear infinite;\n /* 转速:1.6,数字越小转速越快 */\n}\n\n@-webkit-keyframes ccc {\n \n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg)\n }\n/* -1turn 为逆时针转动,1turn 为顺时针转动,相同数字部分记得统一修改: */\n to {\n -webkit-transform: rotate(-1turn);\n transform: rotate(-1turn)\n }\n}\n\n@keyframes ccc {\n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg)\n }\n\n to {\n -webkit-transform: rotate(-1turn);\n transform: rotate(-1turn)\n }\n}\n\n```\n\n\n\n\n\n小风车颜色、大小修改\n\n直接上代码\n\n```css\n#content-inner.layout h1::before {\n color: #ef50a8 ;\n margin-left: -1.55rem;\n font-size: 1.3rem;\n margin-top: -0.23rem;\n}\n#content-inner.layout h2::before {\n color: #fb7061 ;\n margin-left: -1.35rem;\n font-size: 1.1rem;\n margin-top: -0.12rem;\n}\n#content-inner.layout h3::before {\n color: #ffbf00 ;\n margin-left: -1.22rem;\n font-size: 0.95rem;\n margin-top: -0.09rem;\n}\n#content-inner.layout h4::before {\n color: #a9e000 ;\n margin-left: -1.05rem;\n font-size: 0.8rem;\n margin-top: -0.09rem;\n}\n#content-inner.layout h5::before {\n color: #57c850 ;\n margin-left: -0.9rem;\n font-size: 0.7rem;\n margin-top: 0.0rem;\n}\n#content-inner.layout h6::before {\n color: #5ec1e0 ;\n margin-left: -0.9rem;\n font-size: 0.66rem;\n margin-top: 0.0rem;\n}\n\n```\n\n\n\n\n小风车hover效果\n\n鼠标碰到小风车转速变慢及变色\n\n设置鼠标碰到标题时,小风车跟随标题变色,且像是被光标阻碍了,转速变慢。鼠标离开恢复转速。也可以设置为 none 鼠标碰到停止转动。\n\n```css\n\n#content-inner.layout h1:hover, #content-inner.layout h2:hover, #content-inner.layout h3:hover, #content-inner.layout h4:hover, #content-inner.layout h5:hover, #content-inner.layout h6:hover {\n color: #49b1f5 ;\n}\n#content-inner.layout h1:hover::before, #content-inner.layout h2:hover::before, #content-inner.layout h3:hover::before, #content-inner.layout h4:hover::before, #content-inner.layout h5:hover::before, #content-inner.layout h6:hover::before {\n color: #49b1f5 ;\n -webkit-animation: ccc 3.2s linear infinite ;\n animation: ccc 3.2s linear infinite ;\n}\n\n```\n\n\n\n{%endtabs%}\n\n{%endfolding%}","source":"_posts/Hexo/butterfly主题配置.md","raw":"---\ntitle: butterfly主题配置记录\ntags:\n - Hexo\ncategories:\n - Hexo\ndescription: 用于记录搭建Hexo博客的过程即butterfly主题的配置\nabbrlink: dc7f8ae1\ndate: 2021-08-29 10:20:29\nswiper_index: 10\ncover: https://gitee.com/ajream/images/raw/master/img/20210916232736_butterfly2.png\n---\n\n\n\n## 下载安装\n\n{% folding 参考店长文章 %}\n\n\n1. [快速开始](https://butterfly.js.org/posts/21cfbf15/)\n2. [主题页面](https://butterfly.js.org/posts/dc584b87/)\n3. [主题配置1](https://butterfly.js.org/posts/4aa8abbe/)\n4. [主题配置2](https://butterfly.js.org/posts/ceeb73f/)\n5. [进阶教程](https://butterfly.js.org/posts/4073eda/)\n\n{% endfolding %}\n\n\n\n## 页面主题颜色配置\n\n{% folding 配置 %}\n\n\n在主题配置文件`_config.butterfly.yml`修改即可\n\n```yaml\ntheme_color:\n enable: true\n main: \"#49B1F5\"\n paginator: \"#00c4b6\"\n button_hover: \"#FF7242\"\n text_selection: \"#00c4b6\"\n link_color: \"#99a9bf\"\n meta_color: \"#858585\"\n hr_color: \"#A4D8FA\"\n code_foreground: \"#F47466\"\n code_background: \"rgba(27, 31, 35, .05)\"\n toc_color: \"#00c4b6\"\n blockquote_padding_color: \"#49b1f5\"\n blockquote_background_color: \"#49b1f5\"\n\n```\n\n{% endfolding %}\n\n## 外挂标签\n\n参考小康博客\n{% folding 查看%}\n[小康博客butterfly主题使用文档](https://www.antmoe.com/posts/3b43914f/)\n{% endfolding %}\n\n\n## 全局背景透明渐变\n[小康博客](https://www.antmoe.com/posts/7198453/)\n\n\n\n\n\n\n\n\n## 首页置顶轮播图\n\n[糖果屋教程](https://www.akilar.top/posts/8e1264d1/)\n\n{% folding cyan , 步骤 %}\n\n1. 安装插件\n ```\n npm install hexo-butterfly-swiper --save\n ```\n\n2. 在站点文件 `_config.yml`添加如下内容:\n ```yml\n # hexo-butterfly-swiper\n # see https://akilar.top/posts/8e1264d1/\n swiper:\n enable: true # 开关\n priority: 5 #过滤器优先权\n enable_page: all # 应用页面\n timemode: date #date/updated\n layout: # 挂载容器类型\n type: id\n name: recent-posts\n index: 0\n default_descr: 再怎么看我也不知道怎么描述它的啦!\n custom_css: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/css/swiper.min.css #自定义swiper css依赖\n custom_js: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/js/swiper.min.js #自定义swiper js依赖\n ```\n\n| 参数 | 备选值/类型 | 释义 |\n| :------------ | :----------- | :----------------------------------------------------------- |\n| priority | number | 【可选】过滤器优先级,数值越小,执行越早,默认为10,选填 |\n| enable | true/false | 【必选】控制开关 |\n| enable_page | path/all | 【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为all |\n| timemode | date/updated | 【可选】时间显示,date为显示创建日期,updated为显示更新日期,默认为date |\n| layout.type | id/class | 【可选】挂载容器类型,填写id或class,不填则默认为id |\n| layout.name | text | 【必选】挂载容器名称 |\n| layout.index | 0和正整数 | 【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位 |\n| default_descr | text | 默认文章描述 |\n| custom_css | url | 【可选】自定义的swiper依赖项css链接 |\n| custom_js | url | 【可选】自定义的swiper依赖项加js链接 |\n\n3. 在文章的 `front_matter` 添加 `swiper_index` 配置项即可\n\n ```yml\n ---\n title: 文章标题\n date: 创建日期\n updated: 更新日期\n cover: 文章封面\n description: 文章描述\n swiper_index: 1 #置顶轮播图顺序,非负整数,数字越大越靠前\n ---\n ```\n\n{% endfolding %}\n\n\n\n\n\n## 首页卡片浮动显示wowjs\n\n{% folding blue, 插件版本%}\n\n[糖果屋教程](https://akilar.top/posts/abab51cf/)\n\n1. 安装插件\n\n ```\n npm install hexo-butterfly-wowjs --save\n ```\n\n2. 添加配置信息,以下为写法示例\n 在站点配置文件`_config.yml` 或者主题配置文件`_config.butterfly.yml` 中添加\n\n ```yml\n wowjs:\n enable: true #控制动画开关。true是打开,false是关闭\n priority: 10 #过滤器优先级\n mobile: false #移动端是否启用,默认移动端禁用\n animateitem:\n - class: recent-post-item #必填项,需要添加动画的元素的class\n style: animate__zoomIn #必填项,需要添加的动画\n duration: 2s #选填项,动画持续时间,单位可以是ms也可以是s。例如3s,700ms。\n delay: 1s #选填项,动画开始的延迟时间,单位可以是ms也可以是s。例如3s,700ms。\n offset: 100 #选填项,开始动画的距离(相对浏览器底部)\n iteration: 2 #选填项,动画重复的次数\n - class: card-widget\n style: animate__zoomIn\n ```\n\n\n\n| 参数 | 备选值 / 类型 | 释义 |\n| :-------------------- | :--------------------- | :----------------------------------------------------------- |\n| enable | true/false | 【必选】控制开关 |\n| priority | number | 【可选】过滤器优先级,数值越小,执行越早,默认为 10,选填 |\n| mobile | true/false | 控制移动端是否启用,默认移动端禁用 |\n| animateitem.class | class | 【可选】添加动画类名,只支持给 class 添加 |\n| animateitem.style | text | 【可选】动画样式,具体类型参考 [animate.css](https://animate.style/) |\n| animateitem.duration | time, 单位为 s 或 ms | 【可选】动画持续时间,单位可以是 ms 也可以是 s。例如 3s,700ms。 |\n| animateitem.delay | time, 单位为 s 或 ms | 【可选】动画开始的延迟时间,单位可以是 ms 也可以是 s。例如 3s,700ms。 |\n| animateitem.offset | number, 单位为 px | 【可选】开始动画的距离(相对浏览器底部)。 |\n| animateitem.iteration | number, 单位为 s 或 ms | 【可选】动画重复的次数 |\n\n\n\n{%endfolding%}\n\n\n## 看板娘配置\n\n[糖果屋教程](https://akilar.top/posts/5b8f515f/)\n\n\n看板娘不能换装,参考糖果屋的解决方法:\n\n{%folding blue, 点击展开%}\n使用糖果屋大佬配置好的本地化项目的路径:\n修改项目内的 `~\\live2d-widget\\autoload.js`, 修改 `cdnPath`\n\n```diff\n // 加载 waifu.css live2d.min.js waifu-tips.js\n if (screen.width >= 768) {\n \tPromise.all([\n \t\tloadExternalResource(live2d_path + \"waifu.css\", \"css\"),\n \t\tloadExternalResource(live2d_path + \"live2d.min.js\", \"js\"),\n \t\tloadExternalResource(live2d_path + \"waifu-tips.js\", \"js\")\n \t]).then(() => {\n \t\tinitWidget({\n \t\t\twaifuPath: live2d_path + \"waifu-tips.json\",\n \t\t\t//apiPath: \"https://live2d.fghrsh.net/api/\",\n- \t\t\tcdnPath: \"https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/\"\n+ \t\t\tcdnPath: \"https://live2d-api.vercel.app/\"\n//因为jsdelivr不支持50MB以上的包的加速,可能报403错误,所以用的vercel的CDN服务。\n//可以考虑clone大佬配置好的live2d_api仓库自己部署到其他更快的cdn服务上。\n \t\t});\n \t});\n }\n\n```\n\n{%endfolding%}\n\n\n## 主页分类磁铁配置\n\n[糖果屋教程](https://akilar.top/posts/a9131002/)\n\n{%folding blue, 点击展开%}\n\n(1)修改`[Blogroot]\\themes\\butterfly\\layout\\index.pug`, 添加6~8行代码,以下是添加后的结果(注意代码格式比较严格,可直接复制)\n\n```\nextends includes/layout.pug\n\nblock content\n include ./includes/mixins/post-ui.pug\n #recent-posts.recent-posts\n if theme.categoryBar.enable\n .recent-post-item(style='height:auto;width:100%;padding:0px;')\n #categoryBar!= list_categories(site.categories,{class: 'categoryBar',depth: 1})\n +postUI\n include includes/pagination.pug\n\n```\n\n(2)新建`[Blogroot]\\themes\\butterfly\\source\\css\\_layout\\categoryBar.styl`\n\n```\nif hexo-config('categoryBar.enable')\n #categoryBar\n width 100%!important\n ul\n &.categoryBar-list\n margin 5px 5px 0 5px!important\n padding 0!important\n\n li\n &.categoryBar-list-item\n font-weight bold\n display inline-block\n height 180px!important\n margin 5px .5% 0 .5%!important\n background-image linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(16, 16, 16, 0) 100%)\n border-radius 10px\n padding 25px 0 25px 25px!important\n box-shadow rgba(50, 50, 50, 0.3) 50px 50px 50px 50px inset\n overflow hidden\n background-size 100%!important\n background-position center!important\n &:hover\n background-size 110%!important\n box-shadow inset 500px 50px 50px 50px rgba(50,50,50, 0.6)\n span\n &.categoryBar-list-count\n &::after\n transition all .5s\n transform translate(-100%, 0)\n a\n &.categoryBar-list-link\n color white!important\n font-size 20px!important\n &::before\n content '|'!important\n color white!important\n font-size 20px!important\n &:after\n content ''\n position relative\n width 0\n bottom 0\n display block\n height 3px\n border-radius 3px\n background-color white\n &:hover\n &:after\n width 90%\n left 1%\n transition all 0.5s\n\n span\n &.categoryBar-list-count\n display block!important\n color white!important\n font-size 20px!important\n &::before\n content '\\f02d'!important\n padding-right 15px!important\n @extend .fontawesomeIcon\n &::after\n padding 5px\n display block!important\n color white!important\n font-size 20px!important\n position relative\n right -100%\n covers = hexo-config('categoryBar.cover')\n for cover,i in covers\n li.categoryBar-list-item:nth-child({i+1})\n background unquote(cover)\n descrs = hexo-config('categoryBar.descr')\n for descr,i in descrs\n li.categoryBar-list-item:nth-child({i+1})>span::after\n content descr!important\n if hexo-config('categoryBar.column') == 'odd'\n li\n &.categoryBar-list-item\n width 32.3%!important\n else if hexo-config('categoryBar.column') == 'even'\n li\n &.categoryBar-list-item\n width 24%!important\n @media screen and (max-width: 650px)\n li\n &.categoryBar-list-item\n width 48%!important\n height 150px!important\n margin 5px 1% 0 1%!important\n\n $caterow = hexo-config('categoryBar.row')?hexo-config('categoryBar.row'):2\n .categoryBar-list\n max-height 190px * $caterow\n overflow auto\n &::-webkit-scrollbar\n width 0!important\n @media screen and (max-width: 650px)\n .categoryBar-list\n max-height 160px * $caterow\n```\n\n(3)在`[Blogroot]\\_config.butterfly.yml`添加配置项:\n\n```yml\ncategoryBar:\n enable: true\n column: odd # 显示列数,odd:3列 | even:4列\n row: 1 #显示行数,默认两行,超过行数切换为滚动显示\n descr:\n - Hexo博客搭建记录\n - Java学习笔记\n - 电磁场课程学习笔记\n - 硬件学习过程记录\n cover:\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover1.webp')\n - '#abcdef' # HEX格式色值需要用''包裹,不然会被识别成注释\n - rgba(45,67,89,0.7)\n - linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(200,16 , 16, 0) 100%)\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover5.webp')\n - url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover6.webp')\n\n```\n\n{%endfolding%}\n\n\n\n\n## 主页添加文章分类配置(与上一个样式不同)\n\n{%folding blue, 展开%}\n\n[前往小冰博客](https://zfe.space/post/hexo-magnet.html)\n\n效果如下:\n![磁体2.gif](https://cdn.nlark.com/yuque/0/2021/gif/8391485/1615908841586-e6737009-b2dd-4004-ae02-4c6d15f33701.gif)\n这个插件主要实现了以下功能:\n1.自定义 tags 或 categories 的排列和展示 \n2.自定义 tags 或 categories 的展示图标,名称 \n3.自定义排列的行数,默认 2 行\n\n**NPM 插件安装的部署方法**\n\n```powershell\nnpm i hexo-magnet --save\n\n# 或者\n\ncnpm i hexo-magnet --save\n```\n\n注意,一定要加`--save`,不然本地预览的时候可能不会显示!!!\n\n**新增网站根目录\\_config 配置项(不是主题的)**\n\n```yaml\nmagnet:\n enable: true\n priority: 1\n enable_page: /\n type: categories\n devide: 2\n display:\n - name: java\n display_name: aJreamのJava全栈学习\n icon: 📚\n - name: Hexo\n display_name: aJreamのHexo博客搭建&主题配置\n icon: 🎮\n - name: win10\n display_name: aJreamのWin10主题配置\n icon: 💻\n - name: 硬件学习\n display_name: aJreamの硬件学习\n icon: 🔧\n color_setting:\n text_color: black\n text_hover_color: white\n background_color: \"#f2f2f2\"\n background_hover_color: \"#aebfe3\"\n layout:\n type: id\n name: recent-posts\n index: 0\n temple_html: '
${temple_html_item}
'\n plus_style: \n```\n\n接下来来简单说明一下配置项的含义:\n\n> **enable_page**\n\n参数:/\n含义:路由地址,如 / 代表主页。/me/代表自我介绍页等等\n\n> **priority**\n\n参数:1\n含义:插件的叠放顺序,数字越大,叠放约靠前。如果你安装了 hexo-githubcalendar,请将`hexo-githubcalendar`npm 插件更新至`@1.2.3`版本。然后给 hexo-githubcalendar 添加`priority`参数。\n\n```yaml\ngithubcalendar:\n enable: true\n priority: 3 # 这里加上参数\n```\n\n> **type**\n\n参数:categories、tags\n含义:选择筛选分类还是标签\n\n> **devide**\n\n参数:2\n含义:表示分隔的列数,2 表示分为两列展示\n\n> **display**\n\n参数:\n\n```yaml\n- name: 教程 # 这里是tags或者categories的名称\n display_name: 小冰の魔改教程 # 这里是替换的名称\n icon: 📚 # 这里是展示的图标\n```\n\n含义:配置项,可自行设置,按照设置的顺序展示\n\n> **color_setting**\n\n参数:\n\n```yaml\ntext_color: black # 文字默认颜色\ntext_hover_color: white # 文字鼠标悬浮颜色\nbackground_color: \"#f2f2f2\" # 文字背景默认颜色\nbackground_hover_color: \"#b30070\" # 文字背景悬浮颜色\n```\n\n含义:颜色配置项,可自行设置\n\n> **layout**\n\n参数:type; (class&id)\n参数:name;\n参数:index;(数字)\n含义:如果说 gihubcalendar 是一幅画,那么这个 layout 就是指定了哪面墙来挂画\n而在 HTML 的是世界里有两种墙分别 type 为 id 和 class。\n其中在定义 class 的时候会出现多个 class 的情况,这时就需要使用 index,确定是哪一个。\n最后墙的名字即是 name;\n\n```html\n
\n \n
\n
\n \n
\n
\n
\n```\n\n> **temple_html**\n\n参数:html 模板字段\n含义:包含挂载容器\n\n```xml\n
\n
\n ${temple_html_item}\n
\n
\n```\n\n> **plus_style**\n\n参数:\"\"\n含义:提供可自定义的 style,如加入黑夜模式,也可以通过CSS来添加。\n{%endfolding%}\n\n### 添加黑夜模式\n\n在 `custom.css` 文件中添加以下代码,使其在黑夜模式下生效\n```css\n[data-theme=\"dark\"] .magnet_link_context {\n background: #201e1e;\n border-radius: 6px;\n color: white;\n \n}\n\n[data-theme=\"dark\"] .magnet_link_context:hover {\n background: #3b4042;\n}\n```\n修改各类的跳转链接:\n原本点击各个分类后,默认跳转的链接是 `_config.yml` 文件中配置的 url值 + \"/categories\" + 【各个分类】\n\n但为了能使部署在不同服务器上能够跳转到自己的分类下,修改了插件`/node_modules/hexo-magnet/index.js`的代码:\n将第61行(或者在附近某行)的代码 `href=\"${hexo.config.url}/${item.path}\"` 的 `${hexo.config.url}`删掉 ,修改后如下:\n\n```js\ntemple_html_item += ``;\n```\n\n\n### hexo 三连\n\n执行 hexo 三连\n\n```powershell\nhexo clean && hexo g && hexo s\n```\n\n即可发现已经成功部署。\n\n\n\n\n## 标题h1~h6美化\n\n[参考大佬](https://guole.fun/posts/butterfly-custom/#%E6%96%87%E7%AB%A0%E9%A1%B5H1-H6%E6%A0%B7%E5%BC%8F%E4%BF%AE%E6%94%B9)\n\n{%folding blue, 展开%}\nButterfly 在 H1~H6 样式上使用了 [fontawesome.com](https://fontawesome.com/v5.15/icons?from=io)上的图标,引用的是 Unicode 形式。可自行查找合适的。\n![image-20210908221739125](https://gitee.com/ajream/images/raw/master/img/20210908221743_image-20210908221739125.png)\n\n{%tabs 小风车, -1%}\n\n本站小风车样式\n\n```yml\nbeautify:\n enable: true\n field: post # site/post\n # title-prefix-icon: '\\f0c1' 原内容\n title-prefix-icon: '\\f185'\n title-prefix-icon-color: '#F47466'\n```\n\n\n\n\n\n让小风车转起来\n\n在 `/css/custom.css` 文件中,加入以下代码即可。\n\n转速、转向修改看注释\n\n\n```css\n/* 文章页H1-H6图标样式效果 */\nh1::before,\nh2::before,\nh3::before,\nh4::before,\nh5::before,\nh6::before {\n -webkit-animation: ccc 1.6s linear infinite;\n animation: ccc 1.6s linear infinite;\n /* 转速:1.6,数字越小转速越快 */\n}\n\n@-webkit-keyframes ccc {\n \n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg)\n }\n/* -1turn 为逆时针转动,1turn 为顺时针转动,相同数字部分记得统一修改: */\n to {\n -webkit-transform: rotate(-1turn);\n transform: rotate(-1turn)\n }\n}\n\n@keyframes ccc {\n 0% {\n -webkit-transform: rotate(0deg);\n transform: rotate(0deg)\n }\n\n to {\n -webkit-transform: rotate(-1turn);\n transform: rotate(-1turn)\n }\n}\n\n```\n\n\n\n\n\n小风车颜色、大小修改\n\n直接上代码\n\n```css\n#content-inner.layout h1::before {\n color: #ef50a8 ;\n margin-left: -1.55rem;\n font-size: 1.3rem;\n margin-top: -0.23rem;\n}\n#content-inner.layout h2::before {\n color: #fb7061 ;\n margin-left: -1.35rem;\n font-size: 1.1rem;\n margin-top: -0.12rem;\n}\n#content-inner.layout h3::before {\n color: #ffbf00 ;\n margin-left: -1.22rem;\n font-size: 0.95rem;\n margin-top: -0.09rem;\n}\n#content-inner.layout h4::before {\n color: #a9e000 ;\n margin-left: -1.05rem;\n font-size: 0.8rem;\n margin-top: -0.09rem;\n}\n#content-inner.layout h5::before {\n color: #57c850 ;\n margin-left: -0.9rem;\n font-size: 0.7rem;\n margin-top: 0.0rem;\n}\n#content-inner.layout h6::before {\n color: #5ec1e0 ;\n margin-left: -0.9rem;\n font-size: 0.66rem;\n margin-top: 0.0rem;\n}\n\n```\n\n\n\n\n小风车hover效果\n\n鼠标碰到小风车转速变慢及变色\n\n设置鼠标碰到标题时,小风车跟随标题变色,且像是被光标阻碍了,转速变慢。鼠标离开恢复转速。也可以设置为 none 鼠标碰到停止转动。\n\n```css\n\n#content-inner.layout h1:hover, #content-inner.layout h2:hover, #content-inner.layout h3:hover, #content-inner.layout h4:hover, #content-inner.layout h5:hover, #content-inner.layout h6:hover {\n color: #49b1f5 ;\n}\n#content-inner.layout h1:hover::before, #content-inner.layout h2:hover::before, #content-inner.layout h3:hover::before, #content-inner.layout h4:hover::before, #content-inner.layout h5:hover::before, #content-inner.layout h6:hover::before {\n color: #49b1f5 ;\n -webkit-animation: ccc 3.2s linear infinite ;\n animation: ccc 3.2s linear infinite ;\n}\n\n```\n\n\n\n{%endtabs%}\n\n{%endfolding%}","slug":"Hexo/butterfly主题配置","published":1,"updated":"2021-09-16T15:28:56.062Z","_id":"cktk8o6tu00h1akve7d054dj8","comments":1,"layout":"post","photos":[],"link":"","content":"

下载安装

\n
参考店长文章 \n \n
\n

页面主题颜色配置

\n
配置 \n
\n

在主题配置文件_config.butterfly.yml修改即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
theme_color:
enable: true
main: "#49B1F5"
paginator: "#00c4b6"
button_hover: "#FF7242"
text_selection: "#00c4b6"
link_color: "#99a9bf"
meta_color: "#858585"
hr_color: "#A4D8FA"
code_foreground: "#F47466"
code_background: "rgba(27, 31, 35, .05)"
toc_color: "#00c4b6"
blockquote_padding_color: "#49b1f5"
blockquote_background_color: "#49b1f5"

\n
\n
\n

外挂标签

\n

参考小康博客

\n
查看 \n \n
\n

全局背景透明渐变

\n

小康博客

\n

首页置顶轮播图

\n

糖果屋教程

\n
步骤 \n
\n
  1. 安装插件

    1
    npm install hexo-butterfly-swiper --save
  2. 在站点文件 _config.yml添加如下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # hexo-butterfly-swiper
    # see https://akilar.top/posts/8e1264d1/
    swiper:
    enable: true # 开关
    priority: 5 #过滤器优先权
    enable_page: all # 应用页面
    timemode: date #date/updated
    layout: # 挂载容器类型
    type: id
    name: recent-posts
    index: 0
    default_descr: 再怎么看我也不知道怎么描述它的啦!
    custom_css: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/css/swiper.min.css #自定义swiper css依赖
    custom_js: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/js/swiper.min.js #自定义swiper js依赖
参数备选值/类型释义
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为10,选填
enabletrue/false【必选】控制开关
enable_pagepath/all【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为all
timemodedate/updated【可选】时间显示,date为显示创建日期,updated为显示更新日期,默认为date
layout.typeid/class【可选】挂载容器类型,填写id或class,不填则默认为id
layout.nametext【必选】挂载容器名称
layout.index0和正整数【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位
default_descrtext默认文章描述
custom_cssurl【可选】自定义的swiper依赖项css链接
custom_jsurl【可选】自定义的swiper依赖项加js链接
  1. 在文章的 front_matter 添加 swiper_index 配置项即可

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    title: 文章标题
    date: 创建日期
    updated: 更新日期
    cover: 文章封面
    description: 文章描述
    swiper_index: 1 #置顶轮播图顺序,非负整数,数字越大越靠前
    ---
\n
\n
\n

首页卡片浮动显示wowjs

\n
插件版本 \n
\n

糖果屋教程

  1. 安装插件

    1
    npm install hexo-butterfly-wowjs --save
  2. 添加配置信息,以下为写法示例
    在站点配置文件_config.yml 或者主题配置文件_config.butterfly.yml 中添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    wowjs:
    enable: true #控制动画开关。true是打开,false是关闭
    priority: 10 #过滤器优先级
    mobile: false #移动端是否启用,默认移动端禁用
    animateitem:
    - class: recent-post-item #必填项,需要添加动画的元素的class
    style: animate__zoomIn #必填项,需要添加的动画
    duration: 2s #选填项,动画持续时间,单位可以是ms也可以是s。例如3s,700ms。
    delay: 1s #选填项,动画开始的延迟时间,单位可以是ms也可以是s。例如3s,700ms。
    offset: 100 #选填项,开始动画的距离(相对浏览器底部)
    iteration: 2 #选填项,动画重复的次数
    - class: card-widget
    style: animate__zoomIn
参数备选值 / 类型释义
enabletrue/false【必选】控制开关
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为 10,选填
mobiletrue/false控制移动端是否启用,默认移动端禁用
animateitem.classclass【可选】添加动画类名,只支持给 class 添加
animateitem.styletext【可选】动画样式,具体类型参考 animate.css
animateitem.durationtime, 单位为 s 或 ms【可选】动画持续时间,单位可以是 ms 也可以是 s。例如 3s,700ms。
animateitem.delaytime, 单位为 s 或 ms【可选】动画开始的延迟时间,单位可以是 ms 也可以是 s。例如 3s,700ms。
animateitem.offsetnumber, 单位为 px【可选】开始动画的距离(相对浏览器底部)。
animateitem.iterationnumber, 单位为 s 或 ms【可选】动画重复的次数
\n
\n
\n

看板娘配置

\n

糖果屋教程

\n

看板娘不能换装,参考糖果屋的解决方法:

\n
点击展开 \n
\n

使用糖果屋大佬配置好的本地化项目的路径:
修改项目内的 ~\\live2d-widget\\autoload.js, 修改 cdnPath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  // 加载 waifu.css live2d.min.js waifu-tips.js
if (screen.width >= 768) {
\tPromise.all([
\t\tloadExternalResource(live2d_path + "waifu.css", "css"),
\t\tloadExternalResource(live2d_path + "live2d.min.js", "js"),
\t\tloadExternalResource(live2d_path + "waifu-tips.js", "js")
\t]).then(() => {
\t\tinitWidget({
\t\t\twaifuPath: live2d_path + "waifu-tips.json",
\t\t\t//apiPath: "https://live2d.fghrsh.net/api/",
- \t\t\tcdnPath: "https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/"
+ \t\t\tcdnPath: "https://live2d-api.vercel.app/"
//因为jsdelivr不支持50MB以上的包的加速,可能报403错误,所以用的vercel的CDN服务。
//可以考虑clone大佬配置好的live2d_api仓库自己部署到其他更快的cdn服务上。
\t\t});
\t});
}

\n
\n
\n

主页分类磁铁配置

\n

糖果屋教程

\n
点击展开 \n
\n

(1)修改[Blogroot]\\themes\\butterfly\\layout\\index.pug, 添加6~8行代码,以下是添加后的结果(注意代码格式比较严格,可直接复制)

1
2
3
4
5
6
7
8
9
10
11
extends includes/layout.pug

block content
include ./includes/mixins/post-ui.pug
#recent-posts.recent-posts
if theme.categoryBar.enable
.recent-post-item(style='height:auto;width:100%;padding:0px;')
#categoryBar!= list_categories(site.categories,{class: 'categoryBar',depth: 1})
+postUI
include includes/pagination.pug

(2)新建[Blogroot]\\themes\\butterfly\\source\\css\\_layout\\categoryBar.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
if hexo-config('categoryBar.enable')
#categoryBar
width 100%!important
ul
&.categoryBar-list
margin 5px 5px 0 5px!important
padding 0!important

li
&.categoryBar-list-item
font-weight bold
display inline-block
height 180px!important
margin 5px .5% 0 .5%!important
background-image linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(16, 16, 16, 0) 100%)
border-radius 10px
padding 25px 0 25px 25px!important
box-shadow rgba(50, 50, 50, 0.3) 50px 50px 50px 50px inset
overflow hidden
background-size 100%!important
background-position center!important
&:hover
background-size 110%!important
box-shadow inset 500px 50px 50px 50px rgba(50,50,50, 0.6)
span
&.categoryBar-list-count
&::after
transition all .5s
transform translate(-100%, 0)
a
&.categoryBar-list-link
color white!important
font-size 20px!important
&::before
content '|'!important
color white!important
font-size 20px!important
&:after
content ''
position relative
width 0
bottom 0
display block
height 3px
border-radius 3px
background-color white
&:hover
&:after
width 90%
left 1%
transition all 0.5s

span
&.categoryBar-list-count
display block!important
color white!important
font-size 20px!important
&::before
content '\\f02d'!important
padding-right 15px!important
@extend .fontawesomeIcon
&::after
padding 5px
display block!important
color white!important
font-size 20px!important
position relative
right -100%
covers = hexo-config('categoryBar.cover')
for cover,i in covers
li.categoryBar-list-item:nth-child({i+1})
background unquote(cover)
descrs = hexo-config('categoryBar.descr')
for descr,i in descrs
li.categoryBar-list-item:nth-child({i+1})>span::after
content descr!important
if hexo-config('categoryBar.column') == 'odd'
li
&.categoryBar-list-item
width 32.3%!important
else if hexo-config('categoryBar.column') == 'even'
li
&.categoryBar-list-item
width 24%!important
@media screen and (max-width: 650px)
li
&.categoryBar-list-item
width 48%!important
height 150px!important
margin 5px 1% 0 1%!important

$caterow = hexo-config('categoryBar.row')?hexo-config('categoryBar.row'):2
.categoryBar-list
max-height 190px * $caterow
overflow auto
&::-webkit-scrollbar
width 0!important
@media screen and (max-width: 650px)
.categoryBar-list
max-height 160px * $caterow

(3)在[Blogroot]\\_config.butterfly.yml添加配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
categoryBar:
enable: true
column: odd # 显示列数,odd:3列 | even:4列
row: 1 #显示行数,默认两行,超过行数切换为滚动显示
descr:
- Hexo博客搭建记录
- Java学习笔记
- 电磁场课程学习笔记
- 硬件学习过程记录
cover:
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover1.webp')
- '#abcdef' # HEX格式色值需要用''包裹,不然会被识别成注释
- rgba(45,67,89,0.7)
- linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(200,16 , 16, 0) 100%)
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover5.webp')
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover6.webp')

\n
\n
\n

主页添加文章分类配置(与上一个样式不同)

\n
展开 \n
\n

前往小冰博客

效果如下:
\"磁体2.gif\"
这个插件主要实现了以下功能:
1.自定义 tags 或 categories 的排列和展示
2.自定义 tags 或 categories 的展示图标,名称
3.自定义排列的行数,默认 2 行

NPM 插件安装的部署方法

1
2
3
4
5
npm i hexo-magnet --save

# 或者

cnpm i hexo-magnet --save

注意,一定要加--save,不然本地预览的时候可能不会显示!!!

新增网站根目录_config 配置项(不是主题的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
magnet:
enable: true
priority: 1
enable_page: /
type: categories
devide: 2
display:
- name: java
display_name: aJreamのJava全栈学习
icon: 📚
- name: Hexo
display_name: aJreamのHexo博客搭建&主题配置
icon: 🎮
- name: win10
display_name: aJreamのWin10主题配置
icon: 💻
- name: 硬件学习
display_name: aJreamの硬件学习
icon: 🔧
color_setting:
text_color: black
text_hover_color: white
background_color: "#f2f2f2"
background_hover_color: "#aebfe3"
layout:
type: id
name: recent-posts
index: 0
temple_html: '<div class="recent-post-item" style="width:100%;height: auto"><div id="catalog_magnet">${temple_html_item}</div></div>'
plus_style:

接下来来简单说明一下配置项的含义:

enable_page

参数:/
含义:路由地址,如 / 代表主页。/me/代表自我介绍页等等

priority

参数:1
含义:插件的叠放顺序,数字越大,叠放约靠前。如果你安装了 hexo-githubcalendar,请将hexo-githubcalendarnpm 插件更新至@1.2.3版本。然后给 hexo-githubcalendar 添加priority参数。

1
2
3
githubcalendar:
enable: true
priority: 3 # 这里加上参数

type

参数:categories、tags
含义:选择筛选分类还是标签

devide

参数:2
含义:表示分隔的列数,2 表示分为两列展示

display

参数:

1
2
3
- name: 教程 # 这里是tags或者categories的名称
display_name: 小冰の魔改教程 # 这里是替换的名称
icon: 📚 # 这里是展示的图标

含义:配置项,可自行设置,按照设置的顺序展示

color_setting

参数:

1
2
3
4
text_color: black # 文字默认颜色
text_hover_color: white # 文字鼠标悬浮颜色
background_color: "#f2f2f2" # 文字背景默认颜色
background_hover_color: "#b30070" # 文字背景悬浮颜色

含义:颜色配置项,可自行设置

layout

参数:type; (class&id)
参数:name;
参数:index;(数字)
含义:如果说 gihubcalendar 是一幅画,那么这个 layout 就是指定了哪面墙来挂画
而在 HTML 的是世界里有两种墙分别 type 为 id 和 class。
其中在定义 class 的时候会出现多个 class 的情况,这时就需要使用 index,确定是哪一个。
最后墙的名字即是 name;

1
2
3
4
5
6
7
8
<div name="我是墙" id="recent-posts">
<!-- id=>type recent-posts=>name -->
<div name="我是画框">
<div name="我是纸">
<!--这里通过js挂载githubcalendar,也就是画画-->
</div>
</div>
</div>

temple_html

参数:html 模板字段
含义:包含挂载容器

1
2
3
4
5
<div class="recent-post-item" style="width:100%;height: auto"> <!--文章容器-->
<div id="catalog_magnet"> <!--挂载容器-->
${temple_html_item}
</div>
</div>

plus_style

参数:“”
含义:提供可自定义的 style,如加入黑夜模式,也可以通过CSS来添加。

\n
\n
\n

添加黑夜模式

\n

custom.css 文件中添加以下代码,使其在黑夜模式下生效

\n
1
2
3
4
5
6
7
8
9
10
[data-theme="dark"] .magnet_link_context {
background: #201e1e;
border-radius: 6px;
color: white;

}

[data-theme="dark"] .magnet_link_context:hover {
background: #3b4042;
}
\n

修改各类的跳转链接:
\n原本点击各个分类后,默认跳转的链接是 _config.yml 文件中配置的 url值 + “/categories” + 【各个分类】

\n

但为了能使部署在不同服务器上能够跳转到自己的分类下,修改了插件/node_modules/hexo-magnet/index.js的代码:
\n将第61行(或者在附近某行)的代码 href="${hexo.config.url}/${item.path}"${hexo.config.url}删掉 ,修改后如下:

\n
1
temple_html_item += `<div class="magnet_item"><a class="magnet_link" href="/${item.path}"><div class="magnet_link_context" style=""><span style="font-weight:500;flex:1">${j.icon} ${j.display_name}${br_devide}(${item.length})</span><span style="padding:0px 4px;border-radius: 8px;"><i class="fas fa-arrow-circle-right"></i></span></div></a></div>`;
\n

hexo 三连

\n

执行 hexo 三连

\n
1
hexo clean && hexo g && hexo s
\n

即可发现已经成功部署。

\n

标题h1~h6美化

\n

参考大佬

\n
展开 \n
\n

Butterfly 在 H1~H6 样式上使用了 fontawesome.com上的图标,引用的是 Unicode 形式。可自行查找合适的。
\"image-20210908221739125\"

本站小风车样式

1
2
3
4
5
6
beautify:
enable: true
field: post # site/post
# title-prefix-icon: '\\f0c1' 原内容
title-prefix-icon: '\\f185'
title-prefix-icon-color: '#F47466'

让小风车转起来

/css/custom.css 文件中,加入以下代码即可。

转速、转向修改看注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* 文章页H1-H6图标样式效果 */
h1::before,
h2::before,
h3::before,
h4::before,
h5::before,
h6::before {
-webkit-animation: ccc 1.6s linear infinite;
animation: ccc 1.6s linear infinite;
/* 转速:1.6,数字越小转速越快 */
}

@-webkit-keyframes ccc {

0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
/* -1turn 为逆时针转动,1turn 为顺时针转动,相同数字部分记得统一修改: */
to {
-webkit-transform: rotate(-1turn);
transform: rotate(-1turn)
}
}

@keyframes ccc {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}

to {
-webkit-transform: rotate(-1turn);
transform: rotate(-1turn)
}
}

小风车颜色、大小修改

直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#content-inner.layout h1::before {
color: #ef50a8 ;
margin-left: -1.55rem;
font-size: 1.3rem;
margin-top: -0.23rem;
}
#content-inner.layout h2::before {
color: #fb7061 ;
margin-left: -1.35rem;
font-size: 1.1rem;
margin-top: -0.12rem;
}
#content-inner.layout h3::before {
color: #ffbf00 ;
margin-left: -1.22rem;
font-size: 0.95rem;
margin-top: -0.09rem;
}
#content-inner.layout h4::before {
color: #a9e000 ;
margin-left: -1.05rem;
font-size: 0.8rem;
margin-top: -0.09rem;
}
#content-inner.layout h5::before {
color: #57c850 ;
margin-left: -0.9rem;
font-size: 0.7rem;
margin-top: 0.0rem;
}
#content-inner.layout h6::before {
color: #5ec1e0 ;
margin-left: -0.9rem;
font-size: 0.66rem;
margin-top: 0.0rem;
}

小风车hover效果

鼠标碰到小风车转速变慢及变色

设置鼠标碰到标题时,小风车跟随标题变色,且像是被光标阻碍了,转速变慢。鼠标离开恢复转速。也可以设置为 none 鼠标碰到停止转动。

1
2
3
4
5
6
7
8
9
10

#content-inner.layout h1:hover, #content-inner.layout h2:hover, #content-inner.layout h3:hover, #content-inner.layout h4:hover, #content-inner.layout h5:hover, #content-inner.layout h6:hover {
color: #49b1f5 ;
}
#content-inner.layout h1:hover::before, #content-inner.layout h2:hover::before, #content-inner.layout h3:hover::before, #content-inner.layout h4:hover::before, #content-inner.layout h5:hover::before, #content-inner.layout h6:hover::before {
color: #49b1f5 ;
-webkit-animation: ccc 3.2s linear infinite ;
animation: ccc 3.2s linear infinite ;
}

\n
\n
","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

下载安装

\n
参考店长文章 \n \n
\n

页面主题颜色配置

\n
配置 \n
\n

在主题配置文件_config.butterfly.yml修改即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
theme_color:
enable: true
main: "#49B1F5"
paginator: "#00c4b6"
button_hover: "#FF7242"
text_selection: "#00c4b6"
link_color: "#99a9bf"
meta_color: "#858585"
hr_color: "#A4D8FA"
code_foreground: "#F47466"
code_background: "rgba(27, 31, 35, .05)"
toc_color: "#00c4b6"
blockquote_padding_color: "#49b1f5"
blockquote_background_color: "#49b1f5"

\n
\n
\n

外挂标签

\n

参考小康博客

\n
查看 \n \n
\n

全局背景透明渐变

\n

小康博客

\n

首页置顶轮播图

\n

糖果屋教程

\n
步骤 \n
\n
  1. 安装插件

    1
    npm install hexo-butterfly-swiper --save
  2. 在站点文件 _config.yml添加如下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # hexo-butterfly-swiper
    # see https://akilar.top/posts/8e1264d1/
    swiper:
    enable: true # 开关
    priority: 5 #过滤器优先权
    enable_page: all # 应用页面
    timemode: date #date/updated
    layout: # 挂载容器类型
    type: id
    name: recent-posts
    index: 0
    default_descr: 再怎么看我也不知道怎么描述它的啦!
    custom_css: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/css/swiper.min.css #自定义swiper css依赖
    custom_js: https://cdnjs.cloudflare.com/ajax/libs/Swiper/4.1.6/js/swiper.min.js #自定义swiper js依赖
参数备选值/类型释义
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为10,选填
enabletrue/false【必选】控制开关
enable_pagepath/all【可选】填写想要应用的页面的相对路径(即路由地址),如根目录就填’/‘,分类页面就填’/categories/‘。若要应用于所有页面,就填’all’,默认为all
timemodedate/updated【可选】时间显示,date为显示创建日期,updated为显示更新日期,默认为date
layout.typeid/class【可选】挂载容器类型,填写id或class,不填则默认为id
layout.nametext【必选】挂载容器名称
layout.index0和正整数【可选】前提是layout.type为class,因为同一页面可能有多个class,此项用来确认究竟排在第几个顺位
default_descrtext默认文章描述
custom_cssurl【可选】自定义的swiper依赖项css链接
custom_jsurl【可选】自定义的swiper依赖项加js链接
  1. 在文章的 front_matter 添加 swiper_index 配置项即可

    1
    2
    3
    4
    5
    6
    7
    8
    ---
    title: 文章标题
    date: 创建日期
    updated: 更新日期
    cover: 文章封面
    description: 文章描述
    swiper_index: 1 #置顶轮播图顺序,非负整数,数字越大越靠前
    ---
\n
\n
\n

首页卡片浮动显示wowjs

\n
插件版本 \n
\n

糖果屋教程

  1. 安装插件

    1
    npm install hexo-butterfly-wowjs --save
  2. 添加配置信息,以下为写法示例
    在站点配置文件_config.yml 或者主题配置文件_config.butterfly.yml 中添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    wowjs:
    enable: true #控制动画开关。true是打开,false是关闭
    priority: 10 #过滤器优先级
    mobile: false #移动端是否启用,默认移动端禁用
    animateitem:
    - class: recent-post-item #必填项,需要添加动画的元素的class
    style: animate__zoomIn #必填项,需要添加的动画
    duration: 2s #选填项,动画持续时间,单位可以是ms也可以是s。例如3s,700ms。
    delay: 1s #选填项,动画开始的延迟时间,单位可以是ms也可以是s。例如3s,700ms。
    offset: 100 #选填项,开始动画的距离(相对浏览器底部)
    iteration: 2 #选填项,动画重复的次数
    - class: card-widget
    style: animate__zoomIn
参数备选值 / 类型释义
enabletrue/false【必选】控制开关
prioritynumber【可选】过滤器优先级,数值越小,执行越早,默认为 10,选填
mobiletrue/false控制移动端是否启用,默认移动端禁用
animateitem.classclass【可选】添加动画类名,只支持给 class 添加
animateitem.styletext【可选】动画样式,具体类型参考 animate.css
animateitem.durationtime, 单位为 s 或 ms【可选】动画持续时间,单位可以是 ms 也可以是 s。例如 3s,700ms。
animateitem.delaytime, 单位为 s 或 ms【可选】动画开始的延迟时间,单位可以是 ms 也可以是 s。例如 3s,700ms。
animateitem.offsetnumber, 单位为 px【可选】开始动画的距离(相对浏览器底部)。
animateitem.iterationnumber, 单位为 s 或 ms【可选】动画重复的次数
\n
\n
\n

看板娘配置

\n

糖果屋教程

\n

看板娘不能换装,参考糖果屋的解决方法:

\n
点击展开 \n
\n

使用糖果屋大佬配置好的本地化项目的路径:
修改项目内的 ~\\live2d-widget\\autoload.js, 修改 cdnPath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  // 加载 waifu.css live2d.min.js waifu-tips.js
if (screen.width >= 768) {
\tPromise.all([
\t\tloadExternalResource(live2d_path + "waifu.css", "css"),
\t\tloadExternalResource(live2d_path + "live2d.min.js", "js"),
\t\tloadExternalResource(live2d_path + "waifu-tips.js", "js")
\t]).then(() => {
\t\tinitWidget({
\t\t\twaifuPath: live2d_path + "waifu-tips.json",
\t\t\t//apiPath: "https://live2d.fghrsh.net/api/",
- \t\t\tcdnPath: "https://cdn.jsdelivr.net/gh/fghrsh/live2d_api/"
+ \t\t\tcdnPath: "https://live2d-api.vercel.app/"
//因为jsdelivr不支持50MB以上的包的加速,可能报403错误,所以用的vercel的CDN服务。
//可以考虑clone大佬配置好的live2d_api仓库自己部署到其他更快的cdn服务上。
\t\t});
\t});
}

\n
\n
\n

主页分类磁铁配置

\n

糖果屋教程

\n
点击展开 \n
\n

(1)修改[Blogroot]\\themes\\butterfly\\layout\\index.pug, 添加6~8行代码,以下是添加后的结果(注意代码格式比较严格,可直接复制)

1
2
3
4
5
6
7
8
9
10
11
extends includes/layout.pug

block content
include ./includes/mixins/post-ui.pug
#recent-posts.recent-posts
if theme.categoryBar.enable
.recent-post-item(style='height:auto;width:100%;padding:0px;')
#categoryBar!= list_categories(site.categories,{class: 'categoryBar',depth: 1})
+postUI
include includes/pagination.pug

(2)新建[Blogroot]\\themes\\butterfly\\source\\css\\_layout\\categoryBar.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
if hexo-config('categoryBar.enable')
#categoryBar
width 100%!important
ul
&.categoryBar-list
margin 5px 5px 0 5px!important
padding 0!important

li
&.categoryBar-list-item
font-weight bold
display inline-block
height 180px!important
margin 5px .5% 0 .5%!important
background-image linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(16, 16, 16, 0) 100%)
border-radius 10px
padding 25px 0 25px 25px!important
box-shadow rgba(50, 50, 50, 0.3) 50px 50px 50px 50px inset
overflow hidden
background-size 100%!important
background-position center!important
&:hover
background-size 110%!important
box-shadow inset 500px 50px 50px 50px rgba(50,50,50, 0.6)
span
&.categoryBar-list-count
&::after
transition all .5s
transform translate(-100%, 0)
a
&.categoryBar-list-link
color white!important
font-size 20px!important
&::before
content '|'!important
color white!important
font-size 20px!important
&:after
content ''
position relative
width 0
bottom 0
display block
height 3px
border-radius 3px
background-color white
&:hover
&:after
width 90%
left 1%
transition all 0.5s

span
&.categoryBar-list-count
display block!important
color white!important
font-size 20px!important
&::before
content '\\f02d'!important
padding-right 15px!important
@extend .fontawesomeIcon
&::after
padding 5px
display block!important
color white!important
font-size 20px!important
position relative
right -100%
covers = hexo-config('categoryBar.cover')
for cover,i in covers
li.categoryBar-list-item:nth-child({i+1})
background unquote(cover)
descrs = hexo-config('categoryBar.descr')
for descr,i in descrs
li.categoryBar-list-item:nth-child({i+1})>span::after
content descr!important
if hexo-config('categoryBar.column') == 'odd'
li
&.categoryBar-list-item
width 32.3%!important
else if hexo-config('categoryBar.column') == 'even'
li
&.categoryBar-list-item
width 24%!important
@media screen and (max-width: 650px)
li
&.categoryBar-list-item
width 48%!important
height 150px!important
margin 5px 1% 0 1%!important

$caterow = hexo-config('categoryBar.row')?hexo-config('categoryBar.row'):2
.categoryBar-list
max-height 190px * $caterow
overflow auto
&::-webkit-scrollbar
width 0!important
@media screen and (max-width: 650px)
.categoryBar-list
max-height 160px * $caterow

(3)在[Blogroot]\\_config.butterfly.yml添加配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
categoryBar:
enable: true
column: odd # 显示列数,odd:3列 | even:4列
row: 1 #显示行数,默认两行,超过行数切换为滚动显示
descr:
- Hexo博客搭建记录
- Java学习笔记
- 电磁场课程学习笔记
- 硬件学习过程记录
cover:
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover1.webp')
- '#abcdef' # HEX格式色值需要用''包裹,不然会被识别成注释
- rgba(45,67,89,0.7)
- linear-gradient(rgba(0, 0, 0, 0.4) 25%, rgba(200,16 , 16, 0) 100%)
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover5.webp')
- url('https://cdn.jsdelivr.net/npm/akilar-candyassets/image/cover6.webp')

\n
\n
\n

主页添加文章分类配置(与上一个样式不同)

\n
展开 \n
\n

前往小冰博客

效果如下:
\"磁体2.gif\"
这个插件主要实现了以下功能:
1.自定义 tags 或 categories 的排列和展示
2.自定义 tags 或 categories 的展示图标,名称
3.自定义排列的行数,默认 2 行

NPM 插件安装的部署方法

1
2
3
4
5
npm i hexo-magnet --save

# 或者

cnpm i hexo-magnet --save

注意,一定要加--save,不然本地预览的时候可能不会显示!!!

新增网站根目录_config 配置项(不是主题的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
magnet:
enable: true
priority: 1
enable_page: /
type: categories
devide: 2
display:
- name: java
display_name: aJreamのJava全栈学习
icon: 📚
- name: Hexo
display_name: aJreamのHexo博客搭建&主题配置
icon: 🎮
- name: win10
display_name: aJreamのWin10主题配置
icon: 💻
- name: 硬件学习
display_name: aJreamの硬件学习
icon: 🔧
color_setting:
text_color: black
text_hover_color: white
background_color: "#f2f2f2"
background_hover_color: "#aebfe3"
layout:
type: id
name: recent-posts
index: 0
temple_html: '<div class="recent-post-item" style="width:100%;height: auto"><div id="catalog_magnet">${temple_html_item}</div></div>'
plus_style:

接下来来简单说明一下配置项的含义:

enable_page

参数:/
含义:路由地址,如 / 代表主页。/me/代表自我介绍页等等

priority

参数:1
含义:插件的叠放顺序,数字越大,叠放约靠前。如果你安装了 hexo-githubcalendar,请将hexo-githubcalendarnpm 插件更新至@1.2.3版本。然后给 hexo-githubcalendar 添加priority参数。

1
2
3
githubcalendar:
enable: true
priority: 3 # 这里加上参数

type

参数:categories、tags
含义:选择筛选分类还是标签

devide

参数:2
含义:表示分隔的列数,2 表示分为两列展示

display

参数:

1
2
3
- name: 教程 # 这里是tags或者categories的名称
display_name: 小冰の魔改教程 # 这里是替换的名称
icon: 📚 # 这里是展示的图标

含义:配置项,可自行设置,按照设置的顺序展示

color_setting

参数:

1
2
3
4
text_color: black # 文字默认颜色
text_hover_color: white # 文字鼠标悬浮颜色
background_color: "#f2f2f2" # 文字背景默认颜色
background_hover_color: "#b30070" # 文字背景悬浮颜色

含义:颜色配置项,可自行设置

layout

参数:type; (class&id)
参数:name;
参数:index;(数字)
含义:如果说 gihubcalendar 是一幅画,那么这个 layout 就是指定了哪面墙来挂画
而在 HTML 的是世界里有两种墙分别 type 为 id 和 class。
其中在定义 class 的时候会出现多个 class 的情况,这时就需要使用 index,确定是哪一个。
最后墙的名字即是 name;

1
2
3
4
5
6
7
8
<div name="我是墙" id="recent-posts">
<!-- id=>type recent-posts=>name -->
<div name="我是画框">
<div name="我是纸">
<!--这里通过js挂载githubcalendar,也就是画画-->
</div>
</div>
</div>

temple_html

参数:html 模板字段
含义:包含挂载容器

1
2
3
4
5
<div class="recent-post-item" style="width:100%;height: auto"> <!--文章容器-->
<div id="catalog_magnet"> <!--挂载容器-->
${temple_html_item}
</div>
</div>

plus_style

参数:“”
含义:提供可自定义的 style,如加入黑夜模式,也可以通过CSS来添加。

\n
\n
\n

添加黑夜模式

\n

custom.css 文件中添加以下代码,使其在黑夜模式下生效

\n
1
2
3
4
5
6
7
8
9
10
[data-theme="dark"] .magnet_link_context {
background: #201e1e;
border-radius: 6px;
color: white;

}

[data-theme="dark"] .magnet_link_context:hover {
background: #3b4042;
}
\n

修改各类的跳转链接:
\n原本点击各个分类后,默认跳转的链接是 _config.yml 文件中配置的 url值 + “/categories” + 【各个分类】

\n

但为了能使部署在不同服务器上能够跳转到自己的分类下,修改了插件/node_modules/hexo-magnet/index.js的代码:
\n将第61行(或者在附近某行)的代码 href="${hexo.config.url}/${item.path}"${hexo.config.url}删掉 ,修改后如下:

\n
1
temple_html_item += `<div class="magnet_item"><a class="magnet_link" href="/${item.path}"><div class="magnet_link_context" style=""><span style="font-weight:500;flex:1">${j.icon} ${j.display_name}${br_devide}(${item.length})</span><span style="padding:0px 4px;border-radius: 8px;"><i class="fas fa-arrow-circle-right"></i></span></div></a></div>`;
\n

hexo 三连

\n

执行 hexo 三连

\n
1
hexo clean && hexo g && hexo s
\n

即可发现已经成功部署。

\n

标题h1~h6美化

\n

参考大佬

\n
展开 \n
\n

Butterfly 在 H1~H6 样式上使用了 fontawesome.com上的图标,引用的是 Unicode 形式。可自行查找合适的。
\"image-20210908221739125\"

本站小风车样式

1
2
3
4
5
6
beautify:
enable: true
field: post # site/post
# title-prefix-icon: '\\f0c1' 原内容
title-prefix-icon: '\\f185'
title-prefix-icon-color: '#F47466'

让小风车转起来

/css/custom.css 文件中,加入以下代码即可。

转速、转向修改看注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* 文章页H1-H6图标样式效果 */
h1::before,
h2::before,
h3::before,
h4::before,
h5::before,
h6::before {
-webkit-animation: ccc 1.6s linear infinite;
animation: ccc 1.6s linear infinite;
/* 转速:1.6,数字越小转速越快 */
}

@-webkit-keyframes ccc {

0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
/* -1turn 为逆时针转动,1turn 为顺时针转动,相同数字部分记得统一修改: */
to {
-webkit-transform: rotate(-1turn);
transform: rotate(-1turn)
}
}

@keyframes ccc {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}

to {
-webkit-transform: rotate(-1turn);
transform: rotate(-1turn)
}
}

小风车颜色、大小修改

直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#content-inner.layout h1::before {
color: #ef50a8 ;
margin-left: -1.55rem;
font-size: 1.3rem;
margin-top: -0.23rem;
}
#content-inner.layout h2::before {
color: #fb7061 ;
margin-left: -1.35rem;
font-size: 1.1rem;
margin-top: -0.12rem;
}
#content-inner.layout h3::before {
color: #ffbf00 ;
margin-left: -1.22rem;
font-size: 0.95rem;
margin-top: -0.09rem;
}
#content-inner.layout h4::before {
color: #a9e000 ;
margin-left: -1.05rem;
font-size: 0.8rem;
margin-top: -0.09rem;
}
#content-inner.layout h5::before {
color: #57c850 ;
margin-left: -0.9rem;
font-size: 0.7rem;
margin-top: 0.0rem;
}
#content-inner.layout h6::before {
color: #5ec1e0 ;
margin-left: -0.9rem;
font-size: 0.66rem;
margin-top: 0.0rem;
}

小风车hover效果

鼠标碰到小风车转速变慢及变色

设置鼠标碰到标题时,小风车跟随标题变色,且像是被光标阻碍了,转速变慢。鼠标离开恢复转速。也可以设置为 none 鼠标碰到停止转动。

1
2
3
4
5
6
7
8
9
10

#content-inner.layout h1:hover, #content-inner.layout h2:hover, #content-inner.layout h3:hover, #content-inner.layout h4:hover, #content-inner.layout h5:hover, #content-inner.layout h6:hover {
color: #49b1f5 ;
}
#content-inner.layout h1:hover::before, #content-inner.layout h2:hover::before, #content-inner.layout h3:hover::before, #content-inner.layout h4:hover::before, #content-inner.layout h5:hover::before, #content-inner.layout h6:hover::before {
color: #49b1f5 ;
-webkit-animation: ccc 3.2s linear infinite ;
animation: ccc 3.2s linear infinite ;
}

\n
\n
"},{"title":"java面试基础","abbrlink":"af3c95f0","date":"2021-05-21T06:12:33.000Z","description":"关于Java面试会问的一些Java知识点总结,包括类与对象的基本特性、Java虚拟机、字符串、多线程、Java异常、Java IO操作等知识点","_content":"\n\n\n# Java面试基础\n\n\n\n## 面向对象和面向过程的区别\n\n- **面向过程** :**面向过程性能比面向对象高。** 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。**但是**面向过程没有面向对象易维护、易复用、易扩展。\n- **面向对象** :**面向对象易维护、易复用、易扩展。** 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,**面向对象性能比面向过程低**。\n\n## Java语言有哪些特点\n\n1. 面向对象(封装,继承,多态);\n2. 跨平台性( Java 虚拟机实现平台无关性);\n3. 可靠性;\n4. 安全性;\n5. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持)\n6. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);\n7. 编译与解释并存\n\n\n\n## jvm、jdk、jre\n\n- JVM是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。\n\n- JDK 是 Java Development Kit,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。\n- JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。\n\n## Java和C++对比\n\n- 都是面向对象的语言,都支持封装、继承和多态\n- Java 不提供指针来直接访问内存,程序内存更加安全\n- Java 有自动内存管理机制,不需要程序员手动释放无用内存\n- Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。\n\n\n\n## 字符型常量和字符串常量的区别\n\n1. 形式上:字符常量是**单引号**引起的一个字符; 字符串常量是**双引号**引起的若干个字符\n2. 含义上:字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)\n3. 占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节 (**注意: char 在 Java 中占两个字节**)\n\n\n\n## 构造器Constructor是否可被override\n\nConstructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。\n\n\n\n## 重载和重写的区别\n\n重载:同样的一个方法能够根据输入数据的不同,做出不同的处理\n\n重写:当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法\n\n\n\n## Java 面向对象编程三大特性:封装 继承 多态\n\n### 封装\n\n> 1. 属性私有化\n> 2. 提供方法来访问私有属性\n\n封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。\n\n### 继承\n\n继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。\n\n**关于继承如下 3 点请记住:**\n\n1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,**只是拥有**。\n2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。\n3. 子类可以用自己的方式实现父类的方法。\n\n### 多态\n\n所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。\n\n在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。\n\n\n\n## StringBuffer和StringBuilder\n\n两者的区别是什么? String 为什么是不可变的?\n\n### 可变性\n\n简单的来说:`String` 类中使用 `final` 关键字修饰字符数组来保存字符串,`private final char value[]`,所以 String 对象是不可变的。\n\n而 `StringBuilder` 与 `StringBuffer` 都继承自 `AbstractStringBuilder` 类,在 `AbstractStringBuilder` 中也是使用字符数组保存字符串`char[]value` 但是没有用 `final` 关键字修饰,所以这两种对象都是可变的。\n\n`StringBuilder` 与 `StringBuffer` 的构造方法都是调用父类构造方法也就是 `AbstractStringBuilder` 实现的,大家可以自行查阅源码。\n\n### 线程安全性\n\n- `String` 中的对象是不可变的,也就可以理解为常量,线程安全。\n\n- `AbstractStringBuilder` 是 `StringBuilder` 与 `StringBuffer` 的公共父类,定义了一些字符串的基本操作,如 `expandCapacity`、`append`、`insert`、`indexOf` 等公共方法。\n\n- `StringBuffer` 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。\n\n- `StringBuilder` 并没有对方法进行加同步锁,所以是**非**线程安全的。\n\n### 性能\n\n- 每次对 `String` 类型进行改变的时候,都会生成一个新的 `String` 对象,然后将指针指向新的 `String` 对象。\n\n- 而`StringBuffer` 每次都会对 `StringBuffer` 对象本身进行操作,而不是生成新的对象并改变对象引用。\n- 相同情况下使用 `StringBuilder` 相比使用 `StringBuffer` 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。\n\n> 对于三者使用的总结:\n>\n> 1. 操作少量的数据: 适用 `String`\n> 2. 单线程操作字符串缓冲区下操作大量数据: 适用 `StringBuilder`\n> 3. 多线程操作字符串缓冲区下操作大量数据: 适用 `StringBuffer`\n\n## 自动装箱与拆箱\n\n- 装箱:将基本类型用它们对应的引用类型包装起来;\n- 拆箱:将包装类型转换为基本数据类型;\n\n\n\n## 在一个静态方法内调用一个非静态成员为什么是非法的\n\n由于静态方法可以不通过对象进行调用,因此在静态方法里:\n\n- 不能调用其他非静态变量\n- 不可以访问非静态变量成员\n\n## 在Java中定义一个不做事且没有参数的构造方法的作用\n\nJava 程序在实例化子类之前(即执行**子类的构造方法**之前),如果没有用 `super()`来调用父类特定的构造方法,则会默认调用父类中“没有参数的构造方法”。\n\n若父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 `super()`来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。\n\n解决办法:在父类里加上一个不做事且没有参数的构造方法。\n\n详细见:[super关键字](D:\\Users\\74452\\Desktop\\JavaLearning\\javaSE\\14-接口与继承.md)\n\n\n\n## 接口和抽象类的区别\n\n1. 接口的方法默认是 `public`,所有方法在接口中不能有实现(Java 8开始接口方法可以有**默认实现**),而抽象类可以有非抽象的方法。\n2. 接口中除了 `static`、`final` 变量,不能有其他变量,而抽象类中则不一定。\n3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 `extends` 关键字扩展多个接口。\n4. 接口方法默认修饰符是 `public`,抽象方法可以有 `public`、`protected` 和 `default` 这些修饰符(抽象方法就是为了被重写所以不能使用 `private` 关键字修饰!)。\n5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。\n\n> 备注:\n>\n> 1. 在 JDK8 中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。\n> 2. jdk9 的接口被允许定义私有方法 。\n\n\n\n## 对象实体与对象引用\n\n- 用new 运算符创建对象实例(对象实例在堆内存中)\n\n- 对象引用指向对象实例(对象引用存放在栈内存中)\n\n一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);\n\n一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。\n\n\n\n## 类的构造方法\n\n类的构造方法主要作用是完成对类对象的初始化工作。\n\n一个类没有声明构造方法也可以执行,因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。\n\n\n\n## 静态方法与实例方法区别\n\n1. 在外部调用静态方法时,可以使用\"`类名.方法名`\"的方式,也可以使用\"`对象名.方法名`\"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。\n2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。\n\n\n\n## 对象相等vs引用相等\n\n对象的相等,指的是内存中存放的内容是否相等。\n\n而引用相等,指的是他们指向的内存地址是否相等。\n\n\n\n## ==号与equals方法\n\n基本数据类型`==`比较的是值,而引用数据类型`==`比较的是内存地址\n\n注意:\n\n1. 类没有覆盖 equals() 方法时, equals() 比较的是两个对象的内存地址,相当于用 `==`号\n2. String 中的 equals 方法是被重写过的,因此String 的 equals 方法比较的是对象的值\n\n3. 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。\n\n\n\n## hashCode 与 equals \n\n为什么重写 `equals` 时必须重写 `hashCode` 方法:\n\n1. hashcode用来记录对象存放的地址(理解可能不到位),如果两个对象相等,则 hashcode 一定也是相同的。\n\n2. 两个对象相等,对两个对象分别调用 equals 方法都返回 true。\n\n3. 但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。\n4. 因此,equals 方法被覆盖过,则 `hashCode` 方法也必须被覆盖\n\n\n\n## Java值传递\n\nJava 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个**拷贝**,即方法不能修改传递给它的任何参数变量的内容。\n\n> 与c/c++不同,c/c++提供了值传递和引用传递\n\n\n\n## 线程、进程\n\n### 简述\n\n**线程(thread)** 是操作系统能够进行运算调度的最小单位\n\n同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。\n\n---\n\n**进程**是系统进行资源( CPU 时间,内存空间,文件,输入输出设备的使用权等等)分配和调度的基本单位\n\n\n\n### 线程生命周期的几种基本状态\n\n1. 线程创建之后它将处于 **NEW(新建)** 状态\n\n2. 调用 `start()` 方法后线程这时候处于 **READY(就绪)** 状态。\n3. 可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 **RUNNING(运行)** 状态。\n4. 线程执行 `wait()`方法之后,线程进入 **WAITING(等待)**状态\n5. 当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。\n6. 线程在执行 Runnable 的`run()`方法之后将会进入到 **TERMINATED(终止)** 状态。\n\n![Java线程状态变迁](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/19-1-29/Java%20%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E5%8F%98%E8%BF%81.png)\n\n\n\n## final关键字\n\nfinal 关键字主要用在三个地方:变量、方法、类。\n\n1. 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。\n2. 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法。\n3. 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。\n\n\n\n\n\n## Java异常处理\n\n结构图:\n\n\"img\"\n\n\n\n\"img\"\n\n\n\n\n\n`Exception` 和 `Error` 二者都是 Java 异常处理的重要子类,各自都包含大量子类。\n\n- `Exception`:程序本身可以处理的异常,可以通过 `catch` 来进行捕获。`Exception` 又可以分为检查异常(必须处理) `Check Exception` 和 不检查异常(可以不处理) `Uncheck Exception`。\n- `Error` :`Error` 属于程序无法处理的错误 ,我们没办法通过 `catch` 来进行捕获 。例如,Java 虚拟机运行错误(`Virtual MachineError`)、虚拟机内存不够错误(`OutOfMemoryError`)、类定义错误(`NoClassDefFoundError`)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。\n\n\n\n1. 检查异常\n\n 除了``RuntimeException`及其子类以外,其他的`Exception`类及其子类都属于检查异常。常见的受检查异常有:\n\n - IO 相关的异常\n - `ClassNotFoundException` \n - `SQLException`……\n\n \n\n2. 不检查异常\n\n `RuntimeException` 及其子类都统称为非受检查异常,例如:\n\n - `NullPointerException`\n - `NumberFormatException`(字符串转换为数字)\n - `ArrayIndexOutOfBoundsException`(数组越界)\n - `ClassCastException`(类型转换错误)\n - `ArithmeticException`(算术错误)等\n\n\n\n**在以下 3 种特殊情况下,`finally` 块不会被执行:**\n\n1. 在 try 或 finally 块中用了 `System.exit(int)`退出程序。但是,如果 `System.exit(int)` 在异常语句之后,`finally` 还是会被执行\n2. 程序所在的线程死亡。\n3. 关闭 CPU。\n\n> **注意:** 当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖try语句的返回值。\n\n\n\n## 获取用键盘输入常用的两种方法\n\n1. 通过 Scanner\n\n```java\nScanner input = new Scanner(System.in);\nString s = input.nextLine();\ninput.close();Copy to clipboardErrorCopied\n```\n\n\n\n2. 通过 BufferedReader\n\n```java\nBufferedReader input = new BufferedReader(new InputStreamReader(System.in));\nString s = input.readLine();\n```\n\n\n\n## Java 中 IO 流分类\n\n- 按照流的流向分,可以分为输入流和输出流;\n- 按照操作单元划分,可以划分为字节流和字符流;\n- 按照流的角色划分为节点流和处理流\n\n\n\n> Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来:\n>\n> - InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。\n> - OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。\n\n\n\n> 为什么要有字符流?\n>\n> 字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。\n>\n> - 音频文件、图片等二进制文件用字节流比较好,\n>\n> - 涉及到字符文本文件使用字符流比较好\n\n","source":"_posts/Java面试/java面试题.md","raw":"---\ntitle: java面试基础\ntags:\n - Java面试\ncategories:\n - java\n - java面试\nabbrlink: af3c95f0\ndate: 2021-05-21 14:12:33\ndescription: 关于Java面试会问的一些Java知识点总结,包括类与对象的基本特性、Java虚拟机、字符串、多线程、Java异常、Java IO操作等知识点\n---\n\n\n\n# Java面试基础\n\n\n\n## 面向对象和面向过程的区别\n\n- **面向过程** :**面向过程性能比面向对象高。** 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。**但是**面向过程没有面向对象易维护、易复用、易扩展。\n- **面向对象** :**面向对象易维护、易复用、易扩展。** 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,**面向对象性能比面向过程低**。\n\n## Java语言有哪些特点\n\n1. 面向对象(封装,继承,多态);\n2. 跨平台性( Java 虚拟机实现平台无关性);\n3. 可靠性;\n4. 安全性;\n5. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持)\n6. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);\n7. 编译与解释并存\n\n\n\n## jvm、jdk、jre\n\n- JVM是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。\n\n- JDK 是 Java Development Kit,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。\n- JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。\n\n## Java和C++对比\n\n- 都是面向对象的语言,都支持封装、继承和多态\n- Java 不提供指针来直接访问内存,程序内存更加安全\n- Java 有自动内存管理机制,不需要程序员手动释放无用内存\n- Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。\n\n\n\n## 字符型常量和字符串常量的区别\n\n1. 形式上:字符常量是**单引号**引起的一个字符; 字符串常量是**双引号**引起的若干个字符\n2. 含义上:字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)\n3. 占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节 (**注意: char 在 Java 中占两个字节**)\n\n\n\n## 构造器Constructor是否可被override\n\nConstructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。\n\n\n\n## 重载和重写的区别\n\n重载:同样的一个方法能够根据输入数据的不同,做出不同的处理\n\n重写:当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法\n\n\n\n## Java 面向对象编程三大特性:封装 继承 多态\n\n### 封装\n\n> 1. 属性私有化\n> 2. 提供方法来访问私有属性\n\n封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。\n\n### 继承\n\n继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。\n\n**关于继承如下 3 点请记住:**\n\n1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,**只是拥有**。\n2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。\n3. 子类可以用自己的方式实现父类的方法。\n\n### 多态\n\n所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。\n\n在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。\n\n\n\n## StringBuffer和StringBuilder\n\n两者的区别是什么? String 为什么是不可变的?\n\n### 可变性\n\n简单的来说:`String` 类中使用 `final` 关键字修饰字符数组来保存字符串,`private final char value[]`,所以 String 对象是不可变的。\n\n而 `StringBuilder` 与 `StringBuffer` 都继承自 `AbstractStringBuilder` 类,在 `AbstractStringBuilder` 中也是使用字符数组保存字符串`char[]value` 但是没有用 `final` 关键字修饰,所以这两种对象都是可变的。\n\n`StringBuilder` 与 `StringBuffer` 的构造方法都是调用父类构造方法也就是 `AbstractStringBuilder` 实现的,大家可以自行查阅源码。\n\n### 线程安全性\n\n- `String` 中的对象是不可变的,也就可以理解为常量,线程安全。\n\n- `AbstractStringBuilder` 是 `StringBuilder` 与 `StringBuffer` 的公共父类,定义了一些字符串的基本操作,如 `expandCapacity`、`append`、`insert`、`indexOf` 等公共方法。\n\n- `StringBuffer` 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。\n\n- `StringBuilder` 并没有对方法进行加同步锁,所以是**非**线程安全的。\n\n### 性能\n\n- 每次对 `String` 类型进行改变的时候,都会生成一个新的 `String` 对象,然后将指针指向新的 `String` 对象。\n\n- 而`StringBuffer` 每次都会对 `StringBuffer` 对象本身进行操作,而不是生成新的对象并改变对象引用。\n- 相同情况下使用 `StringBuilder` 相比使用 `StringBuffer` 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。\n\n> 对于三者使用的总结:\n>\n> 1. 操作少量的数据: 适用 `String`\n> 2. 单线程操作字符串缓冲区下操作大量数据: 适用 `StringBuilder`\n> 3. 多线程操作字符串缓冲区下操作大量数据: 适用 `StringBuffer`\n\n## 自动装箱与拆箱\n\n- 装箱:将基本类型用它们对应的引用类型包装起来;\n- 拆箱:将包装类型转换为基本数据类型;\n\n\n\n## 在一个静态方法内调用一个非静态成员为什么是非法的\n\n由于静态方法可以不通过对象进行调用,因此在静态方法里:\n\n- 不能调用其他非静态变量\n- 不可以访问非静态变量成员\n\n## 在Java中定义一个不做事且没有参数的构造方法的作用\n\nJava 程序在实例化子类之前(即执行**子类的构造方法**之前),如果没有用 `super()`来调用父类特定的构造方法,则会默认调用父类中“没有参数的构造方法”。\n\n若父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 `super()`来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。\n\n解决办法:在父类里加上一个不做事且没有参数的构造方法。\n\n详细见:[super关键字](D:\\Users\\74452\\Desktop\\JavaLearning\\javaSE\\14-接口与继承.md)\n\n\n\n## 接口和抽象类的区别\n\n1. 接口的方法默认是 `public`,所有方法在接口中不能有实现(Java 8开始接口方法可以有**默认实现**),而抽象类可以有非抽象的方法。\n2. 接口中除了 `static`、`final` 变量,不能有其他变量,而抽象类中则不一定。\n3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 `extends` 关键字扩展多个接口。\n4. 接口方法默认修饰符是 `public`,抽象方法可以有 `public`、`protected` 和 `default` 这些修饰符(抽象方法就是为了被重写所以不能使用 `private` 关键字修饰!)。\n5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。\n\n> 备注:\n>\n> 1. 在 JDK8 中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。\n> 2. jdk9 的接口被允许定义私有方法 。\n\n\n\n## 对象实体与对象引用\n\n- 用new 运算符创建对象实例(对象实例在堆内存中)\n\n- 对象引用指向对象实例(对象引用存放在栈内存中)\n\n一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);\n\n一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。\n\n\n\n## 类的构造方法\n\n类的构造方法主要作用是完成对类对象的初始化工作。\n\n一个类没有声明构造方法也可以执行,因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。\n\n\n\n## 静态方法与实例方法区别\n\n1. 在外部调用静态方法时,可以使用\"`类名.方法名`\"的方式,也可以使用\"`对象名.方法名`\"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。\n2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。\n\n\n\n## 对象相等vs引用相等\n\n对象的相等,指的是内存中存放的内容是否相等。\n\n而引用相等,指的是他们指向的内存地址是否相等。\n\n\n\n## ==号与equals方法\n\n基本数据类型`==`比较的是值,而引用数据类型`==`比较的是内存地址\n\n注意:\n\n1. 类没有覆盖 equals() 方法时, equals() 比较的是两个对象的内存地址,相当于用 `==`号\n2. String 中的 equals 方法是被重写过的,因此String 的 equals 方法比较的是对象的值\n\n3. 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。\n\n\n\n## hashCode 与 equals \n\n为什么重写 `equals` 时必须重写 `hashCode` 方法:\n\n1. hashcode用来记录对象存放的地址(理解可能不到位),如果两个对象相等,则 hashcode 一定也是相同的。\n\n2. 两个对象相等,对两个对象分别调用 equals 方法都返回 true。\n\n3. 但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。\n4. 因此,equals 方法被覆盖过,则 `hashCode` 方法也必须被覆盖\n\n\n\n## Java值传递\n\nJava 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个**拷贝**,即方法不能修改传递给它的任何参数变量的内容。\n\n> 与c/c++不同,c/c++提供了值传递和引用传递\n\n\n\n## 线程、进程\n\n### 简述\n\n**线程(thread)** 是操作系统能够进行运算调度的最小单位\n\n同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。\n\n---\n\n**进程**是系统进行资源( CPU 时间,内存空间,文件,输入输出设备的使用权等等)分配和调度的基本单位\n\n\n\n### 线程生命周期的几种基本状态\n\n1. 线程创建之后它将处于 **NEW(新建)** 状态\n\n2. 调用 `start()` 方法后线程这时候处于 **READY(就绪)** 状态。\n3. 可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 **RUNNING(运行)** 状态。\n4. 线程执行 `wait()`方法之后,线程进入 **WAITING(等待)**状态\n5. 当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 **BLOCKED(阻塞)** 状态。\n6. 线程在执行 Runnable 的`run()`方法之后将会进入到 **TERMINATED(终止)** 状态。\n\n![Java线程状态变迁](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/19-1-29/Java%20%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E5%8F%98%E8%BF%81.png)\n\n\n\n## final关键字\n\nfinal 关键字主要用在三个地方:变量、方法、类。\n\n1. 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。\n2. 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法。\n3. 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。\n\n\n\n\n\n## Java异常处理\n\n结构图:\n\n\"img\"\n\n\n\n\"img\"\n\n\n\n\n\n`Exception` 和 `Error` 二者都是 Java 异常处理的重要子类,各自都包含大量子类。\n\n- `Exception`:程序本身可以处理的异常,可以通过 `catch` 来进行捕获。`Exception` 又可以分为检查异常(必须处理) `Check Exception` 和 不检查异常(可以不处理) `Uncheck Exception`。\n- `Error` :`Error` 属于程序无法处理的错误 ,我们没办法通过 `catch` 来进行捕获 。例如,Java 虚拟机运行错误(`Virtual MachineError`)、虚拟机内存不够错误(`OutOfMemoryError`)、类定义错误(`NoClassDefFoundError`)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。\n\n\n\n1. 检查异常\n\n 除了``RuntimeException`及其子类以外,其他的`Exception`类及其子类都属于检查异常。常见的受检查异常有:\n\n - IO 相关的异常\n - `ClassNotFoundException` \n - `SQLException`……\n\n \n\n2. 不检查异常\n\n `RuntimeException` 及其子类都统称为非受检查异常,例如:\n\n - `NullPointerException`\n - `NumberFormatException`(字符串转换为数字)\n - `ArrayIndexOutOfBoundsException`(数组越界)\n - `ClassCastException`(类型转换错误)\n - `ArithmeticException`(算术错误)等\n\n\n\n**在以下 3 种特殊情况下,`finally` 块不会被执行:**\n\n1. 在 try 或 finally 块中用了 `System.exit(int)`退出程序。但是,如果 `System.exit(int)` 在异常语句之后,`finally` 还是会被执行\n2. 程序所在的线程死亡。\n3. 关闭 CPU。\n\n> **注意:** 当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖try语句的返回值。\n\n\n\n## 获取用键盘输入常用的两种方法\n\n1. 通过 Scanner\n\n```java\nScanner input = new Scanner(System.in);\nString s = input.nextLine();\ninput.close();Copy to clipboardErrorCopied\n```\n\n\n\n2. 通过 BufferedReader\n\n```java\nBufferedReader input = new BufferedReader(new InputStreamReader(System.in));\nString s = input.readLine();\n```\n\n\n\n## Java 中 IO 流分类\n\n- 按照流的流向分,可以分为输入流和输出流;\n- 按照操作单元划分,可以划分为字节流和字符流;\n- 按照流的角色划分为节点流和处理流\n\n\n\n> Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来:\n>\n> - InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。\n> - OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。\n\n\n\n> 为什么要有字符流?\n>\n> 字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。\n>\n> - 音频文件、图片等二进制文件用字节流比较好,\n>\n> - 涉及到字符文本文件使用字符流比较好\n\n","slug":"Java面试/java面试题","published":1,"updated":"2021-09-04T02:01:08.705Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6tu00h3akve8scabvi5","content":"

Java面试基础

面向对象和面向过程的区别

    \n
  • 面向过程面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。但是面向过程没有面向对象易维护、易复用、易扩展。
  • \n
  • 面向对象面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低
  • \n
\n

Java语言有哪些特点

    \n
  1. 面向对象(封装,继承,多态);
  2. \n
  3. 跨平台性( Java 虚拟机实现平台无关性);
  4. \n
  5. 可靠性;
  6. \n
  7. 安全性;
  8. \n
  9. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持)
  10. \n
  11. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);
  12. \n
  13. 编译与解释并存
  14. \n
\n

jvm、jdk、jre

    \n
  • JVM是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。

    \n
  • \n
  • JDK 是 Java Development Kit,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。

    \n
  • \n
  • JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。
  • \n
\n

Java和C++对比

    \n
  • 都是面向对象的语言,都支持封装、继承和多态
  • \n
  • Java 不提供指针来直接访问内存,程序内存更加安全
  • \n
  • Java 有自动内存管理机制,不需要程序员手动释放无用内存
  • \n
  • Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
  • \n
\n

字符型常量和字符串常量的区别

    \n
  1. 形式上:字符常量是单引号引起的一个字符; 字符串常量是双引号引起的若干个字符
  2. \n
  3. 含义上:字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)
  4. \n
  5. 占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节 (注意: char 在 Java 中占两个字节)
  6. \n
\n

构造器Constructor是否可被override

Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。

\n

重载和重写的区别

重载:同样的一个方法能够根据输入数据的不同,做出不同的处理

\n

重写:当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法

\n

Java 面向对象编程三大特性:封装 继承 多态

封装

\n
    \n
  1. 属性私有化
  2. \n
  3. 提供方法来访问私有属性
  4. \n
\n
\n

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

\n

继承

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

\n

关于继承如下 3 点请记住:

\n
    \n
  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有
  2. \n
  3. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
  4. \n
  5. 子类可以用自己的方式实现父类的方法。
  6. \n
\n

多态

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

\n

在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

\n

StringBuffer和StringBuilder

两者的区别是什么? String 为什么是不可变的?

\n

可变性

简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。

\n

StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。

\n

StringBuilderStringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,大家可以自行查阅源码。

\n

线程安全性

    \n
  • String 中的对象是不可变的,也就可以理解为常量,线程安全。

    \n
  • \n
  • AbstractStringBuilderStringBuilderStringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacityappendinsertindexOf 等公共方法。

    \n
  • \n
  • StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。

    \n
  • \n
  • StringBuilder 并没有对方法进行加同步锁,所以是线程安全的。

    \n
  • \n
\n

性能

    \n
  • 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。

    \n
  • \n
  • StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。

    \n
  • \n
  • 相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
  • \n
\n
\n

对于三者使用的总结:

\n
    \n
  1. 操作少量的数据: 适用 String
  2. \n
  3. 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  4. \n
  5. 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer
  6. \n
\n
\n

自动装箱与拆箱

    \n
  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • \n
  • 拆箱:将包装类型转换为基本数据类型;
  • \n
\n

在一个静态方法内调用一个非静态成员为什么是非法的

由于静态方法可以不通过对象进行调用,因此在静态方法里:

\n
    \n
  • 不能调用其他非静态变量
  • \n
  • 不可以访问非静态变量成员
  • \n
\n

在Java中定义一个不做事且没有参数的构造方法的作用

Java 程序在实例化子类之前(即执行子类的构造方法之前),如果没有用 super()来调用父类特定的构造方法,则会默认调用父类中“没有参数的构造方法”。

\n

若父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。

\n

解决办法:在父类里加上一个不做事且没有参数的构造方法。

\n

详细见:super关键字

\n

接口和抽象类的区别

    \n
  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  2. \n
  3. 接口中除了 staticfinal 变量,不能有其他变量,而抽象类中则不一定。
  4. \n
  5. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
  6. \n
  7. 接口方法默认修饰符是 public,抽象方法可以有 publicprotecteddefault 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!)。
  8. \n
  9. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
  10. \n
\n
\n

备注:

\n
    \n
  1. 在 JDK8 中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。
  2. \n
  3. jdk9 的接口被允许定义私有方法 。
  4. \n
\n
\n

对象实体与对象引用

    \n
  • 用new 运算符创建对象实例(对象实例在堆内存中)

    \n
  • \n
  • 对象引用指向对象实例(对象引用存放在栈内存中)

    \n
  • \n
\n

一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);

\n

一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。

\n

类的构造方法

类的构造方法主要作用是完成对类对象的初始化工作。

\n

一个类没有声明构造方法也可以执行,因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。

\n

静态方法与实例方法区别

    \n
  1. 在外部调用静态方法时,可以使用”类名.方法名“的方式,也可以使用”对象名.方法名“的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
  2. \n
  3. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。
  4. \n
\n

对象相等vs引用相等

对象的相等,指的是内存中存放的内容是否相等。

\n

而引用相等,指的是他们指向的内存地址是否相等。

\n

==号与equals方法

基本数据类型==比较的是值,而引用数据类型==比较的是内存地址

\n

注意:

\n
    \n
  1. 类没有覆盖 equals() 方法时, equals() 比较的是两个对象的内存地址,相当于用 ==
  2. \n
  3. String 中的 equals 方法是被重写过的,因此String 的 equals 方法比较的是对象的值

    \n
  4. \n
  5. 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。

    \n
  6. \n
\n

hashCode 与 equals

为什么重写 equals 时必须重写 hashCode 方法:

\n
    \n
  1. hashcode用来记录对象存放的地址(理解可能不到位),如果两个对象相等,则 hashcode 一定也是相同的。

    \n
  2. \n
  3. 两个对象相等,对两个对象分别调用 equals 方法都返回 true。

    \n
  4. \n
  5. 但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。

    \n
  6. \n
  7. 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
  8. \n
\n

Java值传递

Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,即方法不能修改传递给它的任何参数变量的内容。

\n
\n

与c/c++不同,c/c++提供了值传递和引用传递

\n
\n

线程、进程

简述

线程(thread) 是操作系统能够进行运算调度的最小单位

\n

同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

\n
\n

进程是系统进行资源( CPU 时间,内存空间,文件,输入输出设备的使用权等等)分配和调度的基本单位

\n

线程生命周期的几种基本状态

    \n
  1. 线程创建之后它将处于 NEW(新建) 状态

    \n
  2. \n
  3. 调用 start() 方法后线程这时候处于 READY(就绪) 状态。

    \n
  4. \n
  5. 可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 RUNNING(运行) 状态。
  6. \n
  7. 线程执行 wait()方法之后,线程进入 WAITING(等待)状态
  8. \n
  9. 当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。
  10. \n
  11. 线程在执行 Runnable 的run()方法之后将会进入到 TERMINATED(终止) 状态。
  12. \n
\n

\"Java线程状态变迁\"

\n

final关键字

final 关键字主要用在三个地方:变量、方法、类。

\n
    \n
  1. 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
  2. \n
  3. 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法。
  4. \n
  5. 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。
  6. \n
\n

Java异常处理

结构图:

\n

\"img\"

\n

\"img\"

\n

ExceptionError 二者都是 Java 异常处理的重要子类,各自都包含大量子类。

\n
    \n
  • Exception:程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为检查异常(必须处理) Check Exception 和 不检查异常(可以不处理) Uncheck Exception
  • \n
  • ErrorError 属于程序无法处理的错误 ,我们没办法通过 catch 来进行捕获 。例如,Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
  • \n
\n
    \n
  1. 检查异常

    \n

    除了`RuntimeException及其子类以外,其他的Exception类及其子类都属于检查异常。常见的受检查异常有:

    \n
      \n
    • IO 相关的异常
    • \n
    • ClassNotFoundException
    • \n
    • SQLException……
    • \n
    \n
  2. \n
\n
    \n
  1. 不检查异常

    \n

    RuntimeException 及其子类都统称为非受检查异常,例如:

    \n
      \n
    • NullPointerException
    • \n
    • NumberFormatException(字符串转换为数字)
    • \n
    • ArrayIndexOutOfBoundsException(数组越界)
    • \n
    • ClassCastException(类型转换错误)
    • \n
    • ArithmeticException(算术错误)等
    • \n
    \n
  2. \n
\n

在以下 3 种特殊情况下,finally 块不会被执行:

\n
    \n
  1. 在 try 或 finally 块中用了 System.exit(int)退出程序。但是,如果 System.exit(int) 在异常语句之后,finally 还是会被执行
  2. \n
  3. 程序所在的线程死亡。
  4. \n
  5. 关闭 CPU。
  6. \n
\n
\n

注意: 当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖try语句的返回值。

\n
\n

获取用键盘输入常用的两种方法

    \n
  1. 通过 Scanner
  2. \n
\n
1
2
3
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();Copy to clipboardErrorCopied
\n
    \n
  1. 通过 BufferedReader
  2. \n
\n
1
2
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
\n

Java 中 IO 流分类

    \n
  • 按照流的流向分,可以分为输入流和输出流;
  • \n
  • 按照操作单元划分,可以划分为字节流和字符流;
  • \n
  • 按照流的角色划分为节点流和处理流
  • \n
\n
\n

Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来:

\n
    \n
  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • \n
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
  • \n
\n

为什么要有字符流?

\n

字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。

\n
    \n
  • 音频文件、图片等二进制文件用字节流比较好,

    \n
  • \n
  • 涉及到字符文本文件使用字符流比较好

    \n
  • \n
\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"cover":"/img/articles2.png","excerpt":"","more":"

Java面试基础

面向对象和面向过程的区别

    \n
  • 面向过程面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。但是面向过程没有面向对象易维护、易复用、易扩展。
  • \n
  • 面向对象面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低
  • \n
\n

Java语言有哪些特点

    \n
  1. 面向对象(封装,继承,多态);
  2. \n
  3. 跨平台性( Java 虚拟机实现平台无关性);
  4. \n
  5. 可靠性;
  6. \n
  7. 安全性;
  8. \n
  9. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持)
  10. \n
  11. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);
  12. \n
  13. 编译与解释并存
  14. \n
\n

jvm、jdk、jre

    \n
  • JVM是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。

    \n
  • \n
  • JDK 是 Java Development Kit,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。

    \n
  • \n
  • JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。
  • \n
\n

Java和C++对比

    \n
  • 都是面向对象的语言,都支持封装、继承和多态
  • \n
  • Java 不提供指针来直接访问内存,程序内存更加安全
  • \n
  • Java 有自动内存管理机制,不需要程序员手动释放无用内存
  • \n
  • Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
  • \n
\n

字符型常量和字符串常量的区别

    \n
  1. 形式上:字符常量是单引号引起的一个字符; 字符串常量是双引号引起的若干个字符
  2. \n
  3. 含义上:字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)
  4. \n
  5. 占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节 (注意: char 在 Java 中占两个字节)
  6. \n
\n

构造器Constructor是否可被override

Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。

\n

重载和重写的区别

重载:同样的一个方法能够根据输入数据的不同,做出不同的处理

\n

重写:当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法

\n

Java 面向对象编程三大特性:封装 继承 多态

封装

\n
    \n
  1. 属性私有化
  2. \n
  3. 提供方法来访问私有属性
  4. \n
\n
\n

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

\n

继承

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

\n

关于继承如下 3 点请记住:

\n
    \n
  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有
  2. \n
  3. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
  4. \n
  5. 子类可以用自己的方式实现父类的方法。
  6. \n
\n

多态

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

\n

在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

\n

StringBuffer和StringBuilder

两者的区别是什么? String 为什么是不可变的?

\n

可变性

简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。

\n

StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。

\n

StringBuilderStringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,大家可以自行查阅源码。

\n

线程安全性

    \n
  • String 中的对象是不可变的,也就可以理解为常量,线程安全。

    \n
  • \n
  • AbstractStringBuilderStringBuilderStringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacityappendinsertindexOf 等公共方法。

    \n
  • \n
  • StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。

    \n
  • \n
  • StringBuilder 并没有对方法进行加同步锁,所以是线程安全的。

    \n
  • \n
\n

性能

    \n
  • 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。

    \n
  • \n
  • StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。

    \n
  • \n
  • 相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
  • \n
\n
\n

对于三者使用的总结:

\n
    \n
  1. 操作少量的数据: 适用 String
  2. \n
  3. 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  4. \n
  5. 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer
  6. \n
\n
\n

自动装箱与拆箱

    \n
  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • \n
  • 拆箱:将包装类型转换为基本数据类型;
  • \n
\n

在一个静态方法内调用一个非静态成员为什么是非法的

由于静态方法可以不通过对象进行调用,因此在静态方法里:

\n
    \n
  • 不能调用其他非静态变量
  • \n
  • 不可以访问非静态变量成员
  • \n
\n

在Java中定义一个不做事且没有参数的构造方法的作用

Java 程序在实例化子类之前(即执行子类的构造方法之前),如果没有用 super()来调用父类特定的构造方法,则会默认调用父类中“没有参数的构造方法”。

\n

若父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。

\n

解决办法:在父类里加上一个不做事且没有参数的构造方法。

\n

详细见:super关键字

\n

接口和抽象类的区别

    \n
  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  2. \n
  3. 接口中除了 staticfinal 变量,不能有其他变量,而抽象类中则不一定。
  4. \n
  5. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
  6. \n
  7. 接口方法默认修饰符是 public,抽象方法可以有 publicprotecteddefault 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!)。
  8. \n
  9. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
  10. \n
\n
\n

备注:

\n
    \n
  1. 在 JDK8 中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。
  2. \n
  3. jdk9 的接口被允许定义私有方法 。
  4. \n
\n
\n

对象实体与对象引用

    \n
  • 用new 运算符创建对象实例(对象实例在堆内存中)

    \n
  • \n
  • 对象引用指向对象实例(对象引用存放在栈内存中)

    \n
  • \n
\n

一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);

\n

一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。

\n

类的构造方法

类的构造方法主要作用是完成对类对象的初始化工作。

\n

一个类没有声明构造方法也可以执行,因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。

\n

静态方法与实例方法区别

    \n
  1. 在外部调用静态方法时,可以使用”类名.方法名“的方式,也可以使用”对象名.方法名“的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
  2. \n
  3. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。
  4. \n
\n

对象相等vs引用相等

对象的相等,指的是内存中存放的内容是否相等。

\n

而引用相等,指的是他们指向的内存地址是否相等。

\n

==号与equals方法

基本数据类型==比较的是值,而引用数据类型==比较的是内存地址

\n

注意:

\n
    \n
  1. 类没有覆盖 equals() 方法时, equals() 比较的是两个对象的内存地址,相当于用 ==
  2. \n
  3. String 中的 equals 方法是被重写过的,因此String 的 equals 方法比较的是对象的值

    \n
  4. \n
  5. 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。

    \n
  6. \n
\n

hashCode 与 equals

为什么重写 equals 时必须重写 hashCode 方法:

\n
    \n
  1. hashcode用来记录对象存放的地址(理解可能不到位),如果两个对象相等,则 hashcode 一定也是相同的。

    \n
  2. \n
  3. 两个对象相等,对两个对象分别调用 equals 方法都返回 true。

    \n
  4. \n
  5. 但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。

    \n
  6. \n
  7. 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
  8. \n
\n

Java值传递

Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,即方法不能修改传递给它的任何参数变量的内容。

\n
\n

与c/c++不同,c/c++提供了值传递和引用传递

\n
\n

线程、进程

简述

线程(thread) 是操作系统能够进行运算调度的最小单位

\n

同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

\n
\n

进程是系统进行资源( CPU 时间,内存空间,文件,输入输出设备的使用权等等)分配和调度的基本单位

\n

线程生命周期的几种基本状态

    \n
  1. 线程创建之后它将处于 NEW(新建) 状态

    \n
  2. \n
  3. 调用 start() 方法后线程这时候处于 READY(就绪) 状态。

    \n
  4. \n
  5. 可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 RUNNING(运行) 状态。
  6. \n
  7. 线程执行 wait()方法之后,线程进入 WAITING(等待)状态
  8. \n
  9. 当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。
  10. \n
  11. 线程在执行 Runnable 的run()方法之后将会进入到 TERMINATED(终止) 状态。
  12. \n
\n

\"Java线程状态变迁\"

\n

final关键字

final 关键字主要用在三个地方:变量、方法、类。

\n
    \n
  1. 对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
  2. \n
  3. 当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员方法都会被隐式地指定为 final 方法。
  4. \n
  5. 使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将 final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行这些优化了)。类中所有的 private 方法都隐式地指定为 final。
  6. \n
\n

Java异常处理

结构图:

\n

\"img\"

\n

\"img\"

\n

ExceptionError 二者都是 Java 异常处理的重要子类,各自都包含大量子类。

\n
    \n
  • Exception:程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为检查异常(必须处理) Check Exception 和 不检查异常(可以不处理) Uncheck Exception
  • \n
  • ErrorError 属于程序无法处理的错误 ,我们没办法通过 catch 来进行捕获 。例如,Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
  • \n
\n
    \n
  1. 检查异常

    \n

    除了`RuntimeException及其子类以外,其他的Exception类及其子类都属于检查异常。常见的受检查异常有:

    \n
      \n
    • IO 相关的异常
    • \n
    • ClassNotFoundException
    • \n
    • SQLException……
    • \n
    \n
  2. \n
\n
    \n
  1. 不检查异常

    \n

    RuntimeException 及其子类都统称为非受检查异常,例如:

    \n
      \n
    • NullPointerException
    • \n
    • NumberFormatException(字符串转换为数字)
    • \n
    • ArrayIndexOutOfBoundsException(数组越界)
    • \n
    • ClassCastException(类型转换错误)
    • \n
    • ArithmeticException(算术错误)等
    • \n
    \n
  2. \n
\n

在以下 3 种特殊情况下,finally 块不会被执行:

\n
    \n
  1. 在 try 或 finally 块中用了 System.exit(int)退出程序。但是,如果 System.exit(int) 在异常语句之后,finally 还是会被执行
  2. \n
  3. 程序所在的线程死亡。
  4. \n
  5. 关闭 CPU。
  6. \n
\n
\n

注意: 当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖try语句的返回值。

\n
\n

获取用键盘输入常用的两种方法

    \n
  1. 通过 Scanner
  2. \n
\n
1
2
3
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();Copy to clipboardErrorCopied
\n
    \n
  1. 通过 BufferedReader
  2. \n
\n
1
2
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
\n

Java 中 IO 流分类

    \n
  • 按照流的流向分,可以分为输入流和输出流;
  • \n
  • 按照操作单元划分,可以划分为字节流和字符流;
  • \n
  • 按照流的角色划分为节点流和处理流
  • \n
\n
\n

Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来:

\n
    \n
  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • \n
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
  • \n
\n

为什么要有字符流?

\n

字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。

\n
    \n
  • 音频文件、图片等二进制文件用字节流比较好,

    \n
  • \n
  • 涉及到字符文本文件使用字符流比较好

    \n
  • \n
\n
\n"},{"title":"mybatis关联查询(四)","description":"多个表之间如何实现关联查询(一对多、多对一、多对多)","abbrlink":"ba43a8dc","date":"2021-08-26T01:12:46.000Z","cover":"https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png","_content":"\n\n## 关联查询\n\n关联查询分为一对多、多对一、多对多的情形,这里\"一\"指的是个别、个例,\"多\"指的是集体\n\n### 一对多\n\n比如一个班级有多个学生,一个学生只对应一个班级,现在有2张表,一张是班级表,一张是学生表,要查询一个学生对应的班级,即一对多(学生是单个,但班级是一个集合)\n\n下面以用户和地区(一个用户对应一个地区,一个地区有多名用户)两张表来进行联表查询:查找某个用户所在的地区\n\n\n\n[项目地址mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master2)\n\n\n{% note info flat %}\n看这篇文章前要先了解关系型数据库的 主键外键\n{% endnote %}\n\n先创建2个表\n\n```sql\nuse mybatis;\n\n# 创建2个表\ncreate table users(\n id int primary key auto_increment,\n name varchar(11) not null\n);\n\ncreate table area(\n id int primary key auto_increment,\n name varchar(11)\n);\n\n\n-- 给users表添加外键\nalter table users add aid int;\nalter table users add constraint area_id foreign key (aid) references area (id);\n\n\n# 插入数据\ninsert into area(id, name) VALUES (2, \"华南\");\ninsert into area(id, name) VALUES (3, \"东北\");\ninsert into area(id, name) VALUES (9, \"华北\");\n\ninsert into users(id, name, aid) VALUES (1, \"小明\", 2);\ninsert into users(id, name, aid) VALUES (2, \"小王\", 9);\ninsert into users(id, name, aid) VALUES (3, \"小虹\", 2);\ninsert into users(id, name, aid) VALUES (4, \"小天\", 3);\n\n\n\nselect u.id, u.name, a.name from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n\n```\n\n\n\n| users | area |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210827202630533](https://gitee.com/ajream/images/raw/master/img/20210827202634_image-20210827202630533.png) | ![image-20210827211945478](https://gitee.com/ajream/images/raw/master/img/20210827211948_image-20210827211945478.png) |\n\n\n\n\n\n#### 环境配置\n\n\n\n导入包 + resources配置\n\n```xml\n\n \n mysql\n mysql-connector-java\n 8.0.26\n \n \n org.mybatis\n mybatis\n 3.5.6\n \n \n org.projectlombok\n lombok\n 1.18.20\n \n \n junit\n junit\n 4.13\n test\n \n\n\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\nmybatis-config.xml\n\n\n\n```xml\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n\n```\n\n\n\n\n\n#### 进行开发\n\n\n\n(1)创建实体类\n\ncom.ajream.entity.Users\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Users {\n private int id;\n private String name;\n private Area area; //一个用户属于某个地区\n}\n\n```\n\n\n\ncom.ajream.entity.Area\n\n```java\npackage com.ajream.entity;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Area {\n private int id;\n private String name;\n private List users; //一个地区会有多个用户, 即一对多\n}\n\n```\n\n\n\n\n\n(2)使用mybatis进行查询\n\n如果要查询小明是哪个地区的,用sql如何写:\n\n```sql\nselect u.id, u.name, a.name from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n```\n\n![image-20210827205234083](https://gitee.com/ajream/images/raw/master/img/20210827205235_image-20210827205234083.png)\n\n\n\n接着用mybatis实现同样的操作:\n\n\n\n创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Users;\n\npublic interface UserDao {\n Users findByName(String name);\n}\n\n```\n\n\n\n创建mapper映射\n\n```xml\n\n\n\n\n\n \n\n```\n\n\n\n注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n(3)测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.entity.Users;\nimport com.ajream.dao.UserDao;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class UserDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = UserDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n UserDao userDao = sqlSession.getMapper(UserDao.class);\n Users user = userDao.findByName(\"小明\");\n System.out.println(user);\n sqlSession.close();\n\n }\n}\n\n```\n\n输出发现地区是null\n\n![image-20210827212140152](https://gitee.com/ajream/images/raw/master/img/20210827212141_image-20210827212140152.png)\n\n\n\n\n\n这是因为mapper的映射没有做好,实际上,mybatis进行映射时,是通过sql语句的返回结果的字段名来进行映射的\n\n比如这句sql代码返回的结果:\n\n```sql\nselect u.id, u.name, a.name as aname from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n```\n\n![image-20210827220105293](https://gitee.com/ajream/images/raw/master/img/20210827220108_image-20210827220105293.png)\n\n字段名有 `id` 、`name`、`aname` 三个\n\n而我们的实体类 Users\n\n```java\npublic class Users {\n private int id;\n private String name;\n private Area area; //一个用户属于某个地区\n}\n```\n\n也有三个属性 `id`, `name`, `area`\n\n因此 进行映射时 `id` 、`name` 都没有问题,而 `area` 无法与 `aname` 进行映射,所以查询结果是 null\n\n\n\n\n\n因此,关键是要做好 `area` 的映射\n\n在mybatis中,使用resultMap标签来处理这种情况\n\n\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n![image-20210827222054544](https://gitee.com/ajream/images/raw/master/img/20210827222057_image-20210827222054544.png)\n\n解释:\n\n`column` 表示字段名,`property`表示对应的实体类(如上面是 `Users`)的属性名\n\n\n\n![image-20210827220105293](https://gitee.com/ajream/images/raw/master/img/20210827220108_image-20210827220105293.png)\n\n如果是复杂类型的属性,如 `area` 的类型是 `Area`,使用 `` 标签,分别(当然,查询了哪个就映射哪个)对里面的属性进行映射\n\n\n\n\n\n\n\n最后测试运行结果如下:\n\n![image-20210827222509289](https://gitee.com/ajream/images/raw/master/img/20210827222510_image-20210827222509289.png)\n\n\n\n\n\n---\n\n\n\n### 多对一\n\n[项目地址mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master3)\n\n上面演示了一对多,即从用户的方面查询出用户是哪个地区的,\n\n接下来演示了多对一的情形,\"多\"即地区,\"一\"即用户个人,所以多对一是查询一个地区的所有用户\n\n\n\n(1)创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Area;\n\npublic interface AreaDao {\n Area findByName(String name);\n}\n\n```\n\n\n\n(2)创建mapper映射\n\n```xml\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n\n```\n\n说明:多对一关系,使用collection标签, ofType表示集合里面的元素类型\n\n\n\n\n\n(3)注册mapper\n\n\n\n![image-20210827232423407](https://gitee.com/ajream/images/raw/master/img/20210827232426_image-20210827232423407.png)\n\n\n\n(4)测试\n\ncom.ajream.test.AreaDaoTest\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AreaDao;\nimport com.ajream.dao.UserDao;\nimport com.ajream.entity.Area;\nimport com.ajream.entity.Users;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class AreaDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = AreaDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n AreaDao areaDao = sqlSession.getMapper(AreaDao.class);\n Area area = areaDao.findByName(\"华南\");\n System.out.println(area);\n sqlSession.close();\n }\n}\n\n```\n\n\n\n测试结果\n\n![image-20210827232632940](https://gitee.com/ajream/images/raw/master/img/20210827232634_image-20210827232632940.png)\n\n\n\n\n\n### 多对多\n\n[项目地址(某个用户购买了多本书) mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master4)\n\n举个例子,以客户买书为例子,书店有很多种书,语文书、数学书、英语书...,一种书可以被多个客户购买,客户也可以借阅多种书\n\n创建3个表,customer表,books表,books和customer相互映射的表(表示某个用户购买的书类型&某种书被哪些用户购买了)\n\n\n\n```sql\ncreate table customer(\n id int primary key auto_increment,\n name varchar(11)\n);\n\ncreate table books(\n id int primary key auto_increment,\n book_name varchar(11)\n);\ncreate table customer_books(\n id int primary key auto_increment,\n cid int,\n bid int\n);\n\ninsert into customer(id, name) VALUES (1, \"小方\"), (2, \"小海\");\ninsert into books(id, book_name) VALUES (1, \"语文\"), (2,\"数学\"), (3, \"英语\");\ninsert into customer_books(id, cid, bid) values (1,1,1), (2,1,2),(3,1,3),(4,2,1),(5,2,2);\n```\n\n\n\n| customer | books | customer_books(小方买了语数英,小海买了语数) |\n| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210828110017226](https://gitee.com/ajream/images/raw/master/img/20210828110018_image-20210828110017226.png) | ![image-20210828105957247](https://gitee.com/ajream/images/raw/master/img/20210828105958_image-20210828105957247.png) | ![image-20210828105921198](https://gitee.com/ajream/images/raw/master/img/20210828105924_image-20210828105921198.png) |\n\n\n\n查找小方买了那些书\n\n```sql\nselect c.id, c.name, b.book_name \nfrom customer c, books b, customer_books cb \nwhere c.name=\"小方\" and c.id=cb.cid and b.id=cb.bid;\n```\n\n\n\n查找买了语文书的有哪些人\n\n\n\n```sql\nselect c.id, c.name, b.book_name \nfrom customer c, books b, customer_books cb \nwhere b.book_name=\"语文\" and c.id=cb.cid and b.id=cb.bid;\n```\n\n\n\n查找结果如下:\n\n| 小方买的书 | 谁买了语文书 |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210828111314291](https://gitee.com/ajream/images/raw/master/img/20210828111317_image-20210828111314291.png) | ![image-20210828111406936](https://gitee.com/ajream/images/raw/master/img/20210828111408_image-20210828111406936.png) |\n\n\n\n如何使用mybatis实现这样的查询?\n\n\n\n(1)环境配置一条龙\n\n实体类\n\ncom.ajream.entity.Books\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Books {\n private int id;\n private String bookName;\n private List customers;\n}\n\n```\n\ncom.ajream.entity.Customer\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Customer {\n private int id;\n private String name;\n private List books;\n}\n\n```\n\n\n\n(2)如何实现mapper映射\n\n\n\n> 查询某某购买了哪些书\n\n注意,返回结果是某某这个人(即Customer, 拥有id,name,books属性),而不是书(Books)\n\n因此,我们的接口命名为CustomerDao,而不是BooksDao\n\n\n\n接口:\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Customer;\n\npublic interface CustomerDao {\n Customer findByName(String name); //查找某某购买了哪些书\n}\n\n```\n\n\n\nmapper映射\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.CustomerDao;\nimport com.ajream.entity.Customer;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class CustomerDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);\n Customer customer = customerDao.findByName(\"小方\");\n System.out.println(customer);\n\n Customer customer1 = customerDao.findByName(\"小海\");\n System.out.println(customer1);\n sqlSession.close();\n }\n}\n\n```\n\n\n\n输出结果:\n\n![image-20210828120305737](https://gitee.com/ajream/images/raw/master/img/20210828120308_image-20210828120305737.png)\n\n\n\n\n\n> 同理,查找谁购买了XXX这本书也是这样\n\n[项目地址(谁购买了XXX这本书)mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master5)\n\n\n\n创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Books;\n\npublic interface BookDao {\n Books findByBookName(String bookName); //返回结果是一本书\n}\n\n```\n\n\n\n创建mapper\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n注册mapper\n\n```\n......\n```\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.BookDao;\nimport com.ajream.entity.Books;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class BookDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n BookDao bookDao = sqlSession.getMapper(BookDao.class);\n Books book = bookDao.findByBookName(\"语文\");\n System.out.println(book);\n\n Books book1 = bookDao.findByBookName(\"数学\");\n System.out.println(book1);\n\n Books book2 = bookDao.findByBookName(\"英语\");\n System.out.println(book2);\n\n sqlSession.close();\n }\n}\n\n```\n\n\n\n输出结果\n\n\n\n![image-20210828122430777](https://gitee.com/ajream/images/raw/master/img/20210828122433_image-20210828122430777.png)\n\n","source":"_posts/Mybatis/mybatis关联查询(四).md","raw":"---\ntitle: mybatis关联查询(四)\ntags:\n - Mybatis\ncategories:\n - - java\n - Mybatis\ndescription: 多个表之间如何实现关联查询(一对多、多对一、多对多)\nabbrlink: ba43a8dc\ndate: 2021-08-26 09:12:46\ncover: https://gitee.com/ajream/images/raw/master/img/20210829224354_mybatis1.png\n---\n\n\n## 关联查询\n\n关联查询分为一对多、多对一、多对多的情形,这里\"一\"指的是个别、个例,\"多\"指的是集体\n\n### 一对多\n\n比如一个班级有多个学生,一个学生只对应一个班级,现在有2张表,一张是班级表,一张是学生表,要查询一个学生对应的班级,即一对多(学生是单个,但班级是一个集合)\n\n下面以用户和地区(一个用户对应一个地区,一个地区有多名用户)两张表来进行联表查询:查找某个用户所在的地区\n\n\n\n[项目地址mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master2)\n\n\n{% note info flat %}\n看这篇文章前要先了解关系型数据库的 主键外键\n{% endnote %}\n\n先创建2个表\n\n```sql\nuse mybatis;\n\n# 创建2个表\ncreate table users(\n id int primary key auto_increment,\n name varchar(11) not null\n);\n\ncreate table area(\n id int primary key auto_increment,\n name varchar(11)\n);\n\n\n-- 给users表添加外键\nalter table users add aid int;\nalter table users add constraint area_id foreign key (aid) references area (id);\n\n\n# 插入数据\ninsert into area(id, name) VALUES (2, \"华南\");\ninsert into area(id, name) VALUES (3, \"东北\");\ninsert into area(id, name) VALUES (9, \"华北\");\n\ninsert into users(id, name, aid) VALUES (1, \"小明\", 2);\ninsert into users(id, name, aid) VALUES (2, \"小王\", 9);\ninsert into users(id, name, aid) VALUES (3, \"小虹\", 2);\ninsert into users(id, name, aid) VALUES (4, \"小天\", 3);\n\n\n\nselect u.id, u.name, a.name from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n\n```\n\n\n\n| users | area |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210827202630533](https://gitee.com/ajream/images/raw/master/img/20210827202634_image-20210827202630533.png) | ![image-20210827211945478](https://gitee.com/ajream/images/raw/master/img/20210827211948_image-20210827211945478.png) |\n\n\n\n\n\n#### 环境配置\n\n\n\n导入包 + resources配置\n\n```xml\n\n \n mysql\n mysql-connector-java\n 8.0.26\n \n \n org.mybatis\n mybatis\n 3.5.6\n \n \n org.projectlombok\n lombok\n 1.18.20\n \n \n junit\n junit\n 4.13\n test\n \n\n\n\n \n \n src/main/resources\n \n **/*.properties\n **/*.xml\n \n true\n \n \n src/main/java\n \n **/*.properties\n **/*.xml\n \n true\n \n \n\n```\n\n\n\nmybatis-config.xml\n\n\n\n```xml\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n\n```\n\n\n\n\n\n#### 进行开发\n\n\n\n(1)创建实体类\n\ncom.ajream.entity.Users\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Users {\n private int id;\n private String name;\n private Area area; //一个用户属于某个地区\n}\n\n```\n\n\n\ncom.ajream.entity.Area\n\n```java\npackage com.ajream.entity;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Area {\n private int id;\n private String name;\n private List users; //一个地区会有多个用户, 即一对多\n}\n\n```\n\n\n\n\n\n(2)使用mybatis进行查询\n\n如果要查询小明是哪个地区的,用sql如何写:\n\n```sql\nselect u.id, u.name, a.name from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n```\n\n![image-20210827205234083](https://gitee.com/ajream/images/raw/master/img/20210827205235_image-20210827205234083.png)\n\n\n\n接着用mybatis实现同样的操作:\n\n\n\n创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Users;\n\npublic interface UserDao {\n Users findByName(String name);\n}\n\n```\n\n\n\n创建mapper映射\n\n```xml\n\n\n\n\n\n \n\n```\n\n\n\n注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n(3)测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.entity.Users;\nimport com.ajream.dao.UserDao;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class UserDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = UserDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n UserDao userDao = sqlSession.getMapper(UserDao.class);\n Users user = userDao.findByName(\"小明\");\n System.out.println(user);\n sqlSession.close();\n\n }\n}\n\n```\n\n输出发现地区是null\n\n![image-20210827212140152](https://gitee.com/ajream/images/raw/master/img/20210827212141_image-20210827212140152.png)\n\n\n\n\n\n这是因为mapper的映射没有做好,实际上,mybatis进行映射时,是通过sql语句的返回结果的字段名来进行映射的\n\n比如这句sql代码返回的结果:\n\n```sql\nselect u.id, u.name, a.name as aname from users u, area a where u.name=\"小明\" and u.aid = a.id ;\n```\n\n![image-20210827220105293](https://gitee.com/ajream/images/raw/master/img/20210827220108_image-20210827220105293.png)\n\n字段名有 `id` 、`name`、`aname` 三个\n\n而我们的实体类 Users\n\n```java\npublic class Users {\n private int id;\n private String name;\n private Area area; //一个用户属于某个地区\n}\n```\n\n也有三个属性 `id`, `name`, `area`\n\n因此 进行映射时 `id` 、`name` 都没有问题,而 `area` 无法与 `aname` 进行映射,所以查询结果是 null\n\n\n\n\n\n因此,关键是要做好 `area` 的映射\n\n在mybatis中,使用resultMap标签来处理这种情况\n\n\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n \n \n\n```\n\n\n\n![image-20210827222054544](https://gitee.com/ajream/images/raw/master/img/20210827222057_image-20210827222054544.png)\n\n解释:\n\n`column` 表示字段名,`property`表示对应的实体类(如上面是 `Users`)的属性名\n\n\n\n![image-20210827220105293](https://gitee.com/ajream/images/raw/master/img/20210827220108_image-20210827220105293.png)\n\n如果是复杂类型的属性,如 `area` 的类型是 `Area`,使用 `` 标签,分别(当然,查询了哪个就映射哪个)对里面的属性进行映射\n\n\n\n\n\n\n\n最后测试运行结果如下:\n\n![image-20210827222509289](https://gitee.com/ajream/images/raw/master/img/20210827222510_image-20210827222509289.png)\n\n\n\n\n\n---\n\n\n\n### 多对一\n\n[项目地址mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master3)\n\n上面演示了一对多,即从用户的方面查询出用户是哪个地区的,\n\n接下来演示了多对一的情形,\"多\"即地区,\"一\"即用户个人,所以多对一是查询一个地区的所有用户\n\n\n\n(1)创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Area;\n\npublic interface AreaDao {\n Area findByName(String name);\n}\n\n```\n\n\n\n(2)创建mapper映射\n\n```xml\n\n\n\n\n\n \n \n \n \n \n \n \n \n \n\n```\n\n说明:多对一关系,使用collection标签, ofType表示集合里面的元素类型\n\n\n\n\n\n(3)注册mapper\n\n\n\n![image-20210827232423407](https://gitee.com/ajream/images/raw/master/img/20210827232426_image-20210827232423407.png)\n\n\n\n(4)测试\n\ncom.ajream.test.AreaDaoTest\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.AreaDao;\nimport com.ajream.dao.UserDao;\nimport com.ajream.entity.Area;\nimport com.ajream.entity.Users;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class AreaDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = AreaDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n AreaDao areaDao = sqlSession.getMapper(AreaDao.class);\n Area area = areaDao.findByName(\"华南\");\n System.out.println(area);\n sqlSession.close();\n }\n}\n\n```\n\n\n\n测试结果\n\n![image-20210827232632940](https://gitee.com/ajream/images/raw/master/img/20210827232634_image-20210827232632940.png)\n\n\n\n\n\n### 多对多\n\n[项目地址(某个用户购买了多本书) mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master4)\n\n举个例子,以客户买书为例子,书店有很多种书,语文书、数学书、英语书...,一种书可以被多个客户购买,客户也可以借阅多种书\n\n创建3个表,customer表,books表,books和customer相互映射的表(表示某个用户购买的书类型&某种书被哪些用户购买了)\n\n\n\n```sql\ncreate table customer(\n id int primary key auto_increment,\n name varchar(11)\n);\n\ncreate table books(\n id int primary key auto_increment,\n book_name varchar(11)\n);\ncreate table customer_books(\n id int primary key auto_increment,\n cid int,\n bid int\n);\n\ninsert into customer(id, name) VALUES (1, \"小方\"), (2, \"小海\");\ninsert into books(id, book_name) VALUES (1, \"语文\"), (2,\"数学\"), (3, \"英语\");\ninsert into customer_books(id, cid, bid) values (1,1,1), (2,1,2),(3,1,3),(4,2,1),(5,2,2);\n```\n\n\n\n| customer | books | customer_books(小方买了语数英,小海买了语数) |\n| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210828110017226](https://gitee.com/ajream/images/raw/master/img/20210828110018_image-20210828110017226.png) | ![image-20210828105957247](https://gitee.com/ajream/images/raw/master/img/20210828105958_image-20210828105957247.png) | ![image-20210828105921198](https://gitee.com/ajream/images/raw/master/img/20210828105924_image-20210828105921198.png) |\n\n\n\n查找小方买了那些书\n\n```sql\nselect c.id, c.name, b.book_name \nfrom customer c, books b, customer_books cb \nwhere c.name=\"小方\" and c.id=cb.cid and b.id=cb.bid;\n```\n\n\n\n查找买了语文书的有哪些人\n\n\n\n```sql\nselect c.id, c.name, b.book_name \nfrom customer c, books b, customer_books cb \nwhere b.book_name=\"语文\" and c.id=cb.cid and b.id=cb.bid;\n```\n\n\n\n查找结果如下:\n\n| 小方买的书 | 谁买了语文书 |\n| :----------------------------------------------------------: | :----------------------------------------------------------: |\n| ![image-20210828111314291](https://gitee.com/ajream/images/raw/master/img/20210828111317_image-20210828111314291.png) | ![image-20210828111406936](https://gitee.com/ajream/images/raw/master/img/20210828111408_image-20210828111406936.png) |\n\n\n\n如何使用mybatis实现这样的查询?\n\n\n\n(1)环境配置一条龙\n\n实体类\n\ncom.ajream.entity.Books\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Books {\n private int id;\n private String bookName;\n private List customers;\n}\n\n```\n\ncom.ajream.entity.Customer\n\n```java\npackage com.ajream.entity;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.util.List;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\npublic class Customer {\n private int id;\n private String name;\n private List books;\n}\n\n```\n\n\n\n(2)如何实现mapper映射\n\n\n\n> 查询某某购买了哪些书\n\n注意,返回结果是某某这个人(即Customer, 拥有id,name,books属性),而不是书(Books)\n\n因此,我们的接口命名为CustomerDao,而不是BooksDao\n\n\n\n接口:\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Customer;\n\npublic interface CustomerDao {\n Customer findByName(String name); //查找某某购买了哪些书\n}\n\n```\n\n\n\nmapper映射\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n\n\n注册mapper\n\n```xml\n\n \n\n```\n\n\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.CustomerDao;\nimport com.ajream.entity.Customer;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class CustomerDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);\n Customer customer = customerDao.findByName(\"小方\");\n System.out.println(customer);\n\n Customer customer1 = customerDao.findByName(\"小海\");\n System.out.println(customer1);\n sqlSession.close();\n }\n}\n\n```\n\n\n\n输出结果:\n\n![image-20210828120305737](https://gitee.com/ajream/images/raw/master/img/20210828120308_image-20210828120305737.png)\n\n\n\n\n\n> 同理,查找谁购买了XXX这本书也是这样\n\n[项目地址(谁购买了XXX这本书)mybatis-04](https://codechina.csdn.net/m0_46079750/mybatis-study/-/tree/master5)\n\n\n\n创建接口\n\n```java\npackage com.ajream.dao;\n\nimport com.ajream.entity.Books;\n\npublic interface BookDao {\n Books findByBookName(String bookName); //返回结果是一本书\n}\n\n```\n\n\n\n创建mapper\n\n```xml\n\n\n\n\n \n \n \n \n \n \n \n \n\n```\n\n\n\n注册mapper\n\n```\n......\n```\n\n\n\n测试\n\n```java\npackage com.ajream.test;\n\nimport com.ajream.dao.BookDao;\nimport com.ajream.entity.Books;\nimport org.apache.ibatis.session.SqlSession;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.apache.ibatis.session.SqlSessionFactoryBuilder;\n\nimport java.io.InputStream;\n\npublic class BookDaoTest {\n public static void main(String[] args) {\n InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream(\"mybatis-config.xml\");\n SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();\n SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);\n SqlSession sqlSession = sqlSessionFactory.openSession();\n\n BookDao bookDao = sqlSession.getMapper(BookDao.class);\n Books book = bookDao.findByBookName(\"语文\");\n System.out.println(book);\n\n Books book1 = bookDao.findByBookName(\"数学\");\n System.out.println(book1);\n\n Books book2 = bookDao.findByBookName(\"英语\");\n System.out.println(book2);\n\n sqlSession.close();\n }\n}\n\n```\n\n\n\n输出结果\n\n\n\n![image-20210828122430777](https://gitee.com/ajream/images/raw/master/img/20210828122433_image-20210828122430777.png)\n\n","slug":"Mybatis/mybatis关联查询(四)","published":1,"updated":"2021-08-30T09:02:15.636Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6tv00h5akvefji7akri","content":"

关联查询

关联查询分为一对多、多对一、多对多的情形,这里”一”指的是个别、个例,”多”指的是集体

\n

一对多

比如一个班级有多个学生,一个学生只对应一个班级,现在有2张表,一张是班级表,一张是学生表,要查询一个学生对应的班级,即一对多(学生是单个,但班级是一个集合)

\n

下面以用户和地区(一个用户对应一个地区,一个地区有多名用户)两张表来进行联表查询:查找某个用户所在的地区

\n

项目地址mybatis-04

\n

看这篇文章前要先了解关系型数据库的 主键外键

\n
\n

先创建2个表

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use mybatis;

# 创建2个表
create table users(
id int primary key auto_increment,
name varchar(11) not null
);

create table area(
id int primary key auto_increment,
name varchar(11)
);


-- 给users表添加外键
alter table users add aid int;
alter table users add constraint area_id foreign key (aid) references area (id);


# 插入数据
insert into area(id, name) VALUES (2, "华南");
insert into area(id, name) VALUES (3, "东北");
insert into area(id, name) VALUES (9, "华北");

insert into users(id, name, aid) VALUES (1, "小明", 2);
insert into users(id, name, aid) VALUES (2, "小王", 9);
insert into users(id, name, aid) VALUES (3, "小虹", 2);
insert into users(id, name, aid) VALUES (4, "小天", 3);



select u.id, u.name, a.name from users u, area a where u.name="小明" and u.aid = a.id ;

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
usersarea
\"image-20210827202630533\"\"image-20210827211945478\"
\n
\n

环境配置

导入包 + resources配置

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

mybatis-config.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!-- 2. 配置mybatis运行环境,可以有多个运行环境,default表示默认的运行环境是...,id是每个运行环境的唯一标识-->
<environments default="development">
<environment id="development">
<!-- 3. 配置JDBC事务管理-->
<transactionManager type="JDBC" />
<!-- 4. 配置JDBC数据源连接池POOLED-->
<dataSource type="POOLED">
<!-- 5. 配置数据库链接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--配置驱动-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>

</environment>
</environments>
</configuration>
\n

进行开发

(1)创建实体类

\n

com.ajream.entity.Users

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
private int id;
private String name;
private Area area; //一个用户属于某个地区
}

\n

com.ajream.entity.Area

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ajream.entity;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Area {
private int id;
private String name;
private List<Users> users; //一个地区会有多个用户, 即一对多
}

\n

(2)使用mybatis进行查询

\n

如果要查询小明是哪个地区的,用sql如何写:

\n
1
select u.id, u.name, a.name from users u, area a where u.name="小明" and u.aid = a.id ;
\n

\"image-20210827205234083\"

\n

接着用mybatis实现同样的操作:

\n

创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Users;

public interface UserDao {
Users findByName(String name);
}

\n

创建mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.UserDao">
<select id="findByName" parameterType="java.lang.String" resultType="com.ajream.entity.Users">
select u.id, u.name, a.name as aname from users u, area a where u.name="小明" and u.aid = a.id ;
</select>
</mapper>
\n

注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/UserDao.xml"/>
</mappers>
\n

(3)测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.ajream.test;

import com.ajream.entity.Users;
import com.ajream.dao.UserDao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class UserDaoTest {
public static void main(String[] args) {
InputStream inputStream = UserDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

UserDao userDao = sqlSession.getMapper(UserDao.class);
Users user = userDao.findByName("小明");
System.out.println(user);
sqlSession.close();

}
}

\n

输出发现地区是null

\n

\"image-20210827212140152\"

\n

这是因为mapper的映射没有做好,实际上,mybatis进行映射时,是通过sql语句的返回结果的字段名来进行映射的

\n

比如这句sql代码返回的结果:

\n
1
select u.id, u.name, a.name as aname from users u, area a where u.name="小明" and u.aid = a.id ;
\n

\"image-20210827220105293\"

\n

字段名有 idnameaname 三个

\n

而我们的实体类 Users

\n
1
2
3
4
5
public class Users {
private int id;
private String name;
private Area area; //一个用户属于某个地区
}
\n

也有三个属性 idnamearea

\n

因此 进行映射时 idname 都没有问题,而 area 无法与 aname 进行映射,所以查询结果是 null

\n

因此,关键是要做好 area 的映射

\n

在mybatis中,使用resultMap标签来处理这种情况

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.UserDao">

<resultMap id="resultUser" type="com.ajream.entity.Users">
<id column="id" property="id"/> <!--主键映射用<id>标签-->
<result column="name" property="name"/> <!--其他键映射都用<result>标签-->
<association property="area" javaType="com.ajream.entity.Area">
<result column="aname" property="name"/>
</association>
</resultMap>

<select id="findByName" parameterType="java.lang.String" resultMap="resultUser"> <!--不使用resultType, 改用resultMap-->
select u.id, u.name, a.name as aname from users u, area a where u.name=#{name} and u.aid = a.id ;
</select>
</mapper>
\n

\"image-20210827222054544\"

\n

解释:

\n

column 表示字段名,property表示对应的实体类(如上面是 Users)的属性名

\n

\"image-20210827220105293\"

\n

如果是复杂类型的属性,如 area 的类型是 Area,使用 <association> 标签,分别(当然,查询了哪个就映射哪个)对里面的属性进行映射

\n

最后测试运行结果如下:

\n

\"image-20210827222509289\"

\n
\n

多对一

项目地址mybatis-04

\n

上面演示了一对多,即从用户的方面查询出用户是哪个地区的,

\n

接下来演示了多对一的情形,”多”即地区,”一”即用户个人,所以多对一是查询一个地区的所有用户

\n

(1)创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Area;

public interface AreaDao {
Area findByName(String name);
}

\n

(2)创建mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.AreaDao">
<resultMap id="resultArea" type="com.ajream.entity.Area">
<result column="aname" property="name"/> <!--除了主键,其他键映射都用<result>标签-->

<collection property="users" ofType="com.ajream.entity.Users"> <!--使用collection标签, ofType表示集合里面的元素类型-->
<id column="id" property="id"/>
<result column="name" property="name"/>
</collection>
</resultMap>
<select id="findByName" parameterType="java.lang.String" resultMap="resultArea">
select u.id, u.name, a.name as aname from users u, area a where a.name=#{name} and u.aid = a.id;
</select>
</mapper>
\n

说明:多对一关系,使用collection标签, ofType表示集合里面的元素类型

\n

(3)注册mapper

\n

\"image-20210827232423407\"

\n

(4)测试

\n

com.ajream.test.AreaDaoTest

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.ajream.test;

import com.ajream.dao.AreaDao;
import com.ajream.dao.UserDao;
import com.ajream.entity.Area;
import com.ajream.entity.Users;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class AreaDaoTest {
public static void main(String[] args) {
InputStream inputStream = AreaDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

AreaDao areaDao = sqlSession.getMapper(AreaDao.class);
Area area = areaDao.findByName("华南");
System.out.println(area);
sqlSession.close();
}
}

\n

测试结果

\n

\"image-20210827232632940\"

\n

多对多

项目地址(某个用户购买了多本书) mybatis-04

\n

举个例子,以客户买书为例子,书店有很多种书,语文书、数学书、英语书…,一种书可以被多个客户购买,客户也可以借阅多种书

\n

创建3个表,customer表,books表,books和customer相互映射的表(表示某个用户购买的书类型&某种书被哪些用户购买了)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
create table customer(
id int primary key auto_increment,
name varchar(11)
);

create table books(
id int primary key auto_increment,
book_name varchar(11)
);
create table customer_books(
id int primary key auto_increment,
cid int,
bid int
);

insert into customer(id, name) VALUES (1, "小方"), (2, "小海");
insert into books(id, book_name) VALUES (1, "语文"), (2,"数学"), (3, "英语");
insert into customer_books(id, cid, bid) values (1,1,1), (2,1,2),(3,1,3),(4,2,1),(5,2,2);
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
customerbookscustomer_books(小方买了语数英,小海买了语数)
\"image-20210828110017226\"\"image-20210828105957247\"\"image-20210828105921198\"
\n
\n

查找小方买了那些书

\n
1
2
3
select c.id, c.name, b.book_name 
from customer c, books b, customer_books cb
where c.name="小方" and c.id=cb.cid and b.id=cb.bid;
\n

查找买了语文书的有哪些人

\n
1
2
3
select c.id, c.name, b.book_name 
from customer c, books b, customer_books cb
where b.book_name="语文" and c.id=cb.cid and b.id=cb.bid;
\n

查找结果如下:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
小方买的书谁买了语文书
\"image-20210828111314291\"\"image-20210828111406936\"
\n
\n

如何使用mybatis实现这样的查询?

\n

(1)环境配置一条龙

\n

实体类

\n

com.ajream.entity.Books

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {
private int id;
private String bookName;
private List<Customer> customers;
}

\n

com.ajream.entity.Customer

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Customer {
private int id;
private String name;
private List<Books> books;
}

\n

(2)如何实现mapper映射

\n
\n

查询某某购买了哪些书

\n
\n

注意,返回结果是某某这个人(即Customer, 拥有id,name,books属性),而不是书(Books)

\n

因此,我们的接口命名为CustomerDao,而不是BooksDao

\n

接口:

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Customer;

public interface CustomerDao {
Customer findByName(String name); //查找某某购买了哪些书
}

\n

mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.CustomerDao">
<resultMap id="resultCustomer" type="com.ajream.entity.Customer"> <!--返回结果是某某,所以type是Customer-->
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="books" ofType="com.ajream.entity.Books"> <!--使用collections标签-->
<result column="book_name" property="bookName"/>
</collection>
</resultMap>
<select id="findByName" parameterType="java.lang.String" resultMap="resultCustomer">
select c.id, c.name, b.book_name
from customer c, books b, customer_books cb
where c.name=#{name} and c.id=cb.cid and b.id=cb.bid;
</select>
</mapper>
\n

注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/CustomerDao.xml"/>
</mappers>
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.ajream.test;

import com.ajream.dao.CustomerDao;
import com.ajream.entity.Customer;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class CustomerDaoTest {
public static void main(String[] args) {
InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);
Customer customer = customerDao.findByName("小方");
System.out.println(customer);

Customer customer1 = customerDao.findByName("小海");
System.out.println(customer1);
sqlSession.close();
}
}

\n

输出结果:

\n

\"image-20210828120305737\"

\n
\n

同理,查找谁购买了XXX这本书也是这样

\n
\n

项目地址(谁购买了XXX这本书)mybatis-04

\n

创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Books;

public interface BookDao {
Books findByBookName(String bookName); //返回结果是一本书
}

\n

创建mapper

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.BookDao">
<resultMap id="resultBook" type="com.ajream.entity.Books">
<result column="book_name" property="bookName"/>
<collection property="customers" ofType="com.ajream.entity.Customer">
<id column="id" property="id"/>
<result column="name" property="name"/>
</collection>
</resultMap>
<select id="findByBookName" resultMap="resultBook">
select c.id, c.name, b.book_name
from customer c, books b, customer_books cb
where b.book_name=#{bookName} and c.id=cb.cid and b.id=cb.bid;
</select>
</mapper>
\n

注册mapper

\n
1
......
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.ajream.test;

import com.ajream.dao.BookDao;
import com.ajream.entity.Books;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class BookDaoTest {
public static void main(String[] args) {
InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

BookDao bookDao = sqlSession.getMapper(BookDao.class);
Books book = bookDao.findByBookName("语文");
System.out.println(book);

Books book1 = bookDao.findByBookName("数学");
System.out.println(book1);

Books book2 = bookDao.findByBookName("英语");
System.out.println(book2);

sqlSession.close();
}
}

\n

输出结果

\n

\"image-20210828122430777\"

\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

关联查询

关联查询分为一对多、多对一、多对多的情形,这里”一”指的是个别、个例,”多”指的是集体

\n

一对多

比如一个班级有多个学生,一个学生只对应一个班级,现在有2张表,一张是班级表,一张是学生表,要查询一个学生对应的班级,即一对多(学生是单个,但班级是一个集合)

\n

下面以用户和地区(一个用户对应一个地区,一个地区有多名用户)两张表来进行联表查询:查找某个用户所在的地区

\n

项目地址mybatis-04

\n

看这篇文章前要先了解关系型数据库的 主键外键

\n
\n

先创建2个表

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use mybatis;

# 创建2个表
create table users(
id int primary key auto_increment,
name varchar(11) not null
);

create table area(
id int primary key auto_increment,
name varchar(11)
);


-- 给users表添加外键
alter table users add aid int;
alter table users add constraint area_id foreign key (aid) references area (id);


# 插入数据
insert into area(id, name) VALUES (2, "华南");
insert into area(id, name) VALUES (3, "东北");
insert into area(id, name) VALUES (9, "华北");

insert into users(id, name, aid) VALUES (1, "小明", 2);
insert into users(id, name, aid) VALUES (2, "小王", 9);
insert into users(id, name, aid) VALUES (3, "小虹", 2);
insert into users(id, name, aid) VALUES (4, "小天", 3);



select u.id, u.name, a.name from users u, area a where u.name="小明" and u.aid = a.id ;

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
usersarea
\"image-20210827202630533\"\"image-20210827211945478\"
\n
\n

环境配置

导入包 + resources配置

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
\n

mybatis-config.xml

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!-- 2. 配置mybatis运行环境,可以有多个运行环境,default表示默认的运行环境是...,id是每个运行环境的唯一标识-->
<environments default="development">
<environment id="development">
<!-- 3. 配置JDBC事务管理-->
<transactionManager type="JDBC" />
<!-- 4. 配置JDBC数据源连接池POOLED-->
<dataSource type="POOLED">
<!-- 5. 配置数据库链接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!--配置驱动-->
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="admin"/>
</dataSource>

</environment>
</environments>
</configuration>
\n

进行开发

(1)创建实体类

\n

com.ajream.entity.Users

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
private int id;
private String name;
private Area area; //一个用户属于某个地区
}

\n

com.ajream.entity.Area

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.ajream.entity;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Area {
private int id;
private String name;
private List<Users> users; //一个地区会有多个用户, 即一对多
}

\n

(2)使用mybatis进行查询

\n

如果要查询小明是哪个地区的,用sql如何写:

\n
1
select u.id, u.name, a.name from users u, area a where u.name="小明" and u.aid = a.id ;
\n

\"image-20210827205234083\"

\n

接着用mybatis实现同样的操作:

\n

创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Users;

public interface UserDao {
Users findByName(String name);
}

\n

创建mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.UserDao">
<select id="findByName" parameterType="java.lang.String" resultType="com.ajream.entity.Users">
select u.id, u.name, a.name as aname from users u, area a where u.name="小明" and u.aid = a.id ;
</select>
</mapper>
\n

注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/UserDao.xml"/>
</mappers>
\n

(3)测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.ajream.test;

import com.ajream.entity.Users;
import com.ajream.dao.UserDao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class UserDaoTest {
public static void main(String[] args) {
InputStream inputStream = UserDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

UserDao userDao = sqlSession.getMapper(UserDao.class);
Users user = userDao.findByName("小明");
System.out.println(user);
sqlSession.close();

}
}

\n

输出发现地区是null

\n

\"image-20210827212140152\"

\n

这是因为mapper的映射没有做好,实际上,mybatis进行映射时,是通过sql语句的返回结果的字段名来进行映射的

\n

比如这句sql代码返回的结果:

\n
1
select u.id, u.name, a.name as aname from users u, area a where u.name="小明" and u.aid = a.id ;
\n

\"image-20210827220105293\"

\n

字段名有 idnameaname 三个

\n

而我们的实体类 Users

\n
1
2
3
4
5
public class Users {
private int id;
private String name;
private Area area; //一个用户属于某个地区
}
\n

也有三个属性 idnamearea

\n

因此 进行映射时 idname 都没有问题,而 area 无法与 aname 进行映射,所以查询结果是 null

\n

因此,关键是要做好 area 的映射

\n

在mybatis中,使用resultMap标签来处理这种情况

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.UserDao">

<resultMap id="resultUser" type="com.ajream.entity.Users">
<id column="id" property="id"/> <!--主键映射用<id>标签-->
<result column="name" property="name"/> <!--其他键映射都用<result>标签-->
<association property="area" javaType="com.ajream.entity.Area">
<result column="aname" property="name"/>
</association>
</resultMap>

<select id="findByName" parameterType="java.lang.String" resultMap="resultUser"> <!--不使用resultType, 改用resultMap-->
select u.id, u.name, a.name as aname from users u, area a where u.name=#{name} and u.aid = a.id ;
</select>
</mapper>
\n

\"image-20210827222054544\"

\n

解释:

\n

column 表示字段名,property表示对应的实体类(如上面是 Users)的属性名

\n

\"image-20210827220105293\"

\n

如果是复杂类型的属性,如 area 的类型是 Area,使用 <association> 标签,分别(当然,查询了哪个就映射哪个)对里面的属性进行映射

\n

最后测试运行结果如下:

\n

\"image-20210827222509289\"

\n
\n

多对一

项目地址mybatis-04

\n

上面演示了一对多,即从用户的方面查询出用户是哪个地区的,

\n

接下来演示了多对一的情形,”多”即地区,”一”即用户个人,所以多对一是查询一个地区的所有用户

\n

(1)创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Area;

public interface AreaDao {
Area findByName(String name);
}

\n

(2)创建mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!--1. 添加约束-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.AreaDao">
<resultMap id="resultArea" type="com.ajream.entity.Area">
<result column="aname" property="name"/> <!--除了主键,其他键映射都用<result>标签-->

<collection property="users" ofType="com.ajream.entity.Users"> <!--使用collection标签, ofType表示集合里面的元素类型-->
<id column="id" property="id"/>
<result column="name" property="name"/>
</collection>
</resultMap>
<select id="findByName" parameterType="java.lang.String" resultMap="resultArea">
select u.id, u.name, a.name as aname from users u, area a where a.name=#{name} and u.aid = a.id;
</select>
</mapper>
\n

说明:多对一关系,使用collection标签, ofType表示集合里面的元素类型

\n

(3)注册mapper

\n

\"image-20210827232423407\"

\n

(4)测试

\n

com.ajream.test.AreaDaoTest

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.ajream.test;

import com.ajream.dao.AreaDao;
import com.ajream.dao.UserDao;
import com.ajream.entity.Area;
import com.ajream.entity.Users;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class AreaDaoTest {
public static void main(String[] args) {
InputStream inputStream = AreaDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

AreaDao areaDao = sqlSession.getMapper(AreaDao.class);
Area area = areaDao.findByName("华南");
System.out.println(area);
sqlSession.close();
}
}

\n

测试结果

\n

\"image-20210827232632940\"

\n

多对多

项目地址(某个用户购买了多本书) mybatis-04

\n

举个例子,以客户买书为例子,书店有很多种书,语文书、数学书、英语书…,一种书可以被多个客户购买,客户也可以借阅多种书

\n

创建3个表,customer表,books表,books和customer相互映射的表(表示某个用户购买的书类型&某种书被哪些用户购买了)

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
create table customer(
id int primary key auto_increment,
name varchar(11)
);

create table books(
id int primary key auto_increment,
book_name varchar(11)
);
create table customer_books(
id int primary key auto_increment,
cid int,
bid int
);

insert into customer(id, name) VALUES (1, "小方"), (2, "小海");
insert into books(id, book_name) VALUES (1, "语文"), (2,"数学"), (3, "英语");
insert into customer_books(id, cid, bid) values (1,1,1), (2,1,2),(3,1,3),(4,2,1),(5,2,2);
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
customerbookscustomer_books(小方买了语数英,小海买了语数)
\"image-20210828110017226\"\"image-20210828105957247\"\"image-20210828105921198\"
\n
\n

查找小方买了那些书

\n
1
2
3
select c.id, c.name, b.book_name 
from customer c, books b, customer_books cb
where c.name="小方" and c.id=cb.cid and b.id=cb.bid;
\n

查找买了语文书的有哪些人

\n
1
2
3
select c.id, c.name, b.book_name 
from customer c, books b, customer_books cb
where b.book_name="语文" and c.id=cb.cid and b.id=cb.bid;
\n

查找结果如下:

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n
小方买的书谁买了语文书
\"image-20210828111314291\"\"image-20210828111406936\"
\n
\n

如何使用mybatis实现这样的查询?

\n

(1)环境配置一条龙

\n

实体类

\n

com.ajream.entity.Books

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {
private int id;
private String bookName;
private List<Customer> customers;
}

\n

com.ajream.entity.Customer

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ajream.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Customer {
private int id;
private String name;
private List<Books> books;
}

\n

(2)如何实现mapper映射

\n
\n

查询某某购买了哪些书

\n
\n

注意,返回结果是某某这个人(即Customer, 拥有id,name,books属性),而不是书(Books)

\n

因此,我们的接口命名为CustomerDao,而不是BooksDao

\n

接口:

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Customer;

public interface CustomerDao {
Customer findByName(String name); //查找某某购买了哪些书
}

\n

mapper映射

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.CustomerDao">
<resultMap id="resultCustomer" type="com.ajream.entity.Customer"> <!--返回结果是某某,所以type是Customer-->
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="books" ofType="com.ajream.entity.Books"> <!--使用collections标签-->
<result column="book_name" property="bookName"/>
</collection>
</resultMap>
<select id="findByName" parameterType="java.lang.String" resultMap="resultCustomer">
select c.id, c.name, b.book_name
from customer c, books b, customer_books cb
where c.name=#{name} and c.id=cb.cid and b.id=cb.bid;
</select>
</mapper>
\n

注册mapper

\n
1
2
3
<mappers>
<mapper resource="com/ajream/dao/CustomerDao.xml"/>
</mappers>
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.ajream.test;

import com.ajream.dao.CustomerDao;
import com.ajream.entity.Customer;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class CustomerDaoTest {
public static void main(String[] args) {
InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);
Customer customer = customerDao.findByName("小方");
System.out.println(customer);

Customer customer1 = customerDao.findByName("小海");
System.out.println(customer1);
sqlSession.close();
}
}

\n

输出结果:

\n

\"image-20210828120305737\"

\n
\n

同理,查找谁购买了XXX这本书也是这样

\n
\n

项目地址(谁购买了XXX这本书)mybatis-04

\n

创建接口

\n
1
2
3
4
5
6
7
8
package com.ajream.dao;

import com.ajream.entity.Books;

public interface BookDao {
Books findByBookName(String bookName); //返回结果是一本书
}

\n

创建mapper

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.ajream.dao.BookDao">
<resultMap id="resultBook" type="com.ajream.entity.Books">
<result column="book_name" property="bookName"/>
<collection property="customers" ofType="com.ajream.entity.Customer">
<id column="id" property="id"/>
<result column="name" property="name"/>
</collection>
</resultMap>
<select id="findByBookName" resultMap="resultBook">
select c.id, c.name, b.book_name
from customer c, books b, customer_books cb
where b.book_name=#{bookName} and c.id=cb.cid and b.id=cb.bid;
</select>
</mapper>
\n

注册mapper

\n
1
......
\n

测试

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.ajream.test;

import com.ajream.dao.BookDao;
import com.ajream.entity.Books;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class BookDaoTest {
public static void main(String[] args) {
InputStream inputStream = CustomerDaoTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

BookDao bookDao = sqlSession.getMapper(BookDao.class);
Books book = bookDao.findByBookName("语文");
System.out.println(book);

Books book1 = bookDao.findByBookName("数学");
System.out.println(book1);

Books book2 = bookDao.findByBookName("英语");
System.out.println(book2);

sqlSession.close();
}
}

\n

输出结果

\n

\"image-20210828122430777\"

\n"},{"title":"14-接口与继承","abbrlink":"ab09b476","date":"2021-02-14T07:38:23.000Z","description":"内容较多,主要是关于类的一些知识:接口和抽象类、子类与父类的关系、内部类等","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n# 接口与继承\n\n## 接口\n\n### 接口\n\n接口的创建:(与创建类相似)\n\n```java\n//创建一个播放器接口\npublic interface Player{\n public void play();\n public void pause();\n public void stop();\n public void tune(); //接口只有方法声明,没有主体,并且默认都是public的,可以不写“public”\n}\n```\n\nJava接口是一系列方法的**声明**(没有主体),是一些方法特征的集合;\n\n一个接口只有方法的特征**没有方法的实现**(不能使用 new来创建实例),因此这些方法可以在不同的地方被不同的类实现(继承),进而在不同的类中实现不同的功能。\n\n接口就是一组抽象方法和常量值的集合。可以把接口看成是一种特殊的抽象类。\n\n(1)其所有的方法都必须是抽象的(abstract)。\n\n(2)其属性成员(若有)只能是final static的常量。\n\n### 实现接口\n\n实现一个接口与类的继承相似,用 `implements` 来实现\n\n```java\n// 创建一个MP3类,实现播放器功能\npublic MP3 implements Player{\n public void play(){\n System.out.println(\"播放\");\n }\n \n public void pause(){\n System.out.println(\"暂停\");\n }\n \n public void stop(){\n System.out.println(\"停止\");\n }\n \n public void tune(){\n System.out.println(\"调节音量\");\n }\n}\n```\n\n\n\n## 对象转型\n\n### 明确引用类型与对象类型的概念\n\n引用和对象都是有类型的\n\n```java\nHero h = new Hero();\n```\n\n在这个例子,引用是 `h`,对象是 `new Hero()`,它们的类型均为 `Hero`\n\n通常情况引用类型与对象类型是一样的,但也有不一样的时候\n\n### 子类转父类(向上转型)\n\n所谓的转型,是指当**引用类型**和**对象类型**不一致的时候,才需要进行类型转换;\n类型转换有时候会成功,有时候会失败\n\n假如 `ADHero` 是 `Hero` 的子类\n\n```java\nHero h = new Hero();\nADHero ad = new ADHero();\n\n```\n\n用图来表示其关系如下:\n\n\"image-20210126224549229\"\n\n```java\nh = ad; //向上转型\n```\n\n转型后的图:\n\n\"image-20210126224655825\"\n\n可以看到 `h` 经过 `ad` 最终指向的还是 `Hero`\n\n\n\n因此,把 `h` 指向 `ADHero` 一定可以,因为 `ADHero` 继承了 `Hero`,Hero能实现的功能 ADHero 也可以\n\n\n\n### 父类转子类(向下转型)\n\n父类转子类,有的时候行,**有的时候不行**,所以必须进行强制转换。\n强制转换的意思就是:转换有风险,风险自担。\n\n假如 `ADHero` 和 `Support` 是 `Hero` 的两个不同子类:\n\n```java\nHero h =new Hero();\nADHero ad = new ADHero();\nSupport s = new Support(); \n```\n\n```java\nh = s; // 向上转型,一定可以\n```\n\n向上转型一定可以,因为 `h`通过 `s` 最终还是指向 `Hero`\n\n\"image-20210126225703371\"\n\n\n\n```java\nad = (ADHero)h; //向下转型,不一定可以,需要强制转换\n```\n\n虽然 `ad` 指向的 `ADHero` 是 `Hero` 的子类,眨眼看来 `ad` 最终也是指向 Hero 的,但实际上指向的是 ADHero,ADHero与Hero还是有区别的,ADHero有的方法Hero不一定有\n\n而ad通过h最终指向的是 Hero,而不是 ADHero,因此会有风险(指向ADHero才不会有风险),只能强制转换。\n\n\"image-20210126230408061\"\n\n因为子类拥有的方法父类不一定有,因此 `h` 向下转型后,`ad`可能就没有了一些子类的方法\n\n### 没有继承关系的两个类\n\n没有继承关系的两个类,互相转换,一定会失败\n\n\n\n### 实现类转换成接口(向上转型)\n\n假如 `AD` 是一个接口,`ADHero` 类继承了它\n\n```java\npublic class Test {\n public static void main(String[] args) {\n ADHero ad = new ADHero();\n \n AD adi = ad; //向上转型 \n } \n}\n```\n\n\n\n### 转型练习\n\n如下转换能否成功?如果不能,是哪一行会出错?为什么会出错?\n\n```java\npublic class Hero {\n public String name;\n protected float hp;\n \n public static void main(String[] args) {\n ADHero ad = new ADHero();\n Hero h = ad;\n AD adi = (AD) h;\n APHero ap = (APHero) adi;\n }\n}\n```\n\n\n\n| 分析 |\n| :----------------------------------------------------------- |\n| 第7行向上转型没问题 |\n| 第8行,可以将h强制转换为AD,因为 AD 与 Hero 之间通过子类关联在了一起;因此 h 指向了AD,而通过 h -> ad -> ADHero -> AD,最终也是指向AD,虽有风险,但不会报错; |\n| 第9行,不能将 adi 强制转换为 APHero,因为 AD 与 APHero之间没有关联,因此ap本应该指向 `APHero`,但通过转换 ap -> adi -> AD,即最终指向了AD,因此不能进行转换,会报错 |\n\n\n\n\n\n\n\n\"image-20210126234924701\"\n\n\n\n### instanceof 语句\n\n`instanceof Hero` 判断一个引用所指向的对象,是否是以下两种,返回 true 或 false\n\n- Hero类型\n- Hero的子类\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public static void main(String[] args) {\n ADHero ad = new ADHero();\n APHero ap = new APHero();\n \n Hero h1= ad;\n Hero h2= ap;\n \n //判断引用h1指向的对象,是否是ADHero类型\n System.out.println(h1 instanceof ADHero); \t//true\n \n //判断引用h2指向的对象,是否是APHero类型\n System.out.println(h2 instanceof APHero);\t//true\n \n //判断引用h1指向的对象,是否是Hero的子类型\n System.out.println(h1 instanceof Hero);\t\t//true\n }\n}\n```\n\n\n\n## 重写\n\n子类可以继承父类的对象方法\n\n在继承后,重复提供该方法,就叫做方法的重写\n\n又叫**覆盖** override\n\n> 父类Item有一个方法,叫做effect\n>\n> ```java\n> package property;\n> \n> public class Item {\n> String name;\n> int price;\n> \n> public void buy(){\n> System.out.println(\"购买\");\n> }\n> public void effect() {\n> System.out.println(\"物品使用后,可以有效果\");\n> }\n> \n> }\n> ```\n>\n> 子类LifePotion继承Item,同时也提供了方法effect\n>\n> ```java\n> package property;\n> \n> public class LifePotion extends Item{\n> \n> public void effect(){ \t\t\t\t\t\t//重写effect()方法\n> System.out.println(\"血瓶使用后,可以回血\");\n> }\n> \n> }\n> ```\n>\n> 调用重写的方法\n> 调用就会执行重写的方法,而不是从父类的方法\n\n重写的优点:\n\n如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。\n\n但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能**放弃继承Item**,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动.\n\n这样就增加了开发时间和维护成本\n\n\n\n## 多态\n\n### 操作符的多态\n\n同一个操作符在不同情境下,具备不同的作用:\n\n- 如果+号两侧都是整型,那么`+`代表 数字相加\n- 如果+号两侧,**任意**一个是字符串,那么`+`代表字符串连接\n\n### 类的多态\n\n多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态(实际是子类重写了父类的方法,导致子类的方法与父类的方法不同,子类之间的方法也不同)\n\n**类的多态**即父类引用指向不同的子类对象,调用同一个方法时出现不同的效果\n\n> 假设 Hero是 `ADHero` 与 `APHero` 的父类\n>\n> ```java\n> Hero h1 = new ADHero();\n> Hero h2 = new APHero();\n> ```\n\n类的多态的条件:\n\n1. 父类(接口)引用指向子类对象\n\n2. 调用的方法有[重写](#14.3 重写)\n\n### 使用类多态的好处\n\n如果不使用多态,例如:\n\n> 假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法\n> useLifePotion、useMagicPotion;\n> 除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如\n> usePurityPotion、useGuard、useInvisiblePotion等等等等\n\n```java\npackage charactor;\n \nimport property.LifePotion;\nimport property.MagicPotion;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void useLifePotion(LifePotion lp){\n lp.effect();\n }\n public void useMagicPotion(MagicPotion mp){\n mp.effect();\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n \n LifePotion lp =new LifePotion();\n MagicPotion mp =new MagicPotion();\n \n garen.useLifePotion(lp);\n garen.useMagicPotion(mp);\n \n }\n \n}\n```\n\n这个时候采用**多态**来解决这个问题:\n\n> 设计一个方法叫做useItem,其参数类型是Item\n>\n> - 如果是使用血瓶,调用该方法\n> - 如果是使用魔瓶,还是调用该方法\n> - 无论英雄要使用什么样的物品,**只需要一个方法** 即可\n\n```java\npackage charactor;\n \nimport property.Item;\nimport property.LifePotion;\nimport property.MagicPotion;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void useItem(Item i){ //设计一个方法叫做useItem,其参数类型是Item\n i.effect();\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n \n LifePotion lp =new LifePotion();\n MagicPotion mp =new MagicPotion();\n \n garen.useItem(lp); //使用血瓶lp\n garen.useItem(mp); //使用魔瓶mp\n \n }\n \n}\n```\n\n\n\n## 隐藏\n\n与重写类似:\n\n- **重写,是**子类覆盖父类的**对象方法**;\n- **隐藏**,就是子类覆盖父类的**类方法**\n\n如何隐藏呢:\n\n> 父类有一个类方法 :battleWin\n>\n> ```java\n> package charactor;\n> \n> public class Hero {\n> public String name;\n> protected float hp;\n> \n> public static void battleWin(){ //类方法,静态方法\n> System.out.println(\"hero battle win\");\n> }\n> \n> }\n> ```\n>\n> 创建一个子类,隐藏父类方法 `battleWin()`\n>\n> ```java\n> package charactor;\n> \n> public class ADHero extends Hero implements AD{\n> \n> //隐藏父类的battleWin方法\n> public static void battleWin(){\n> System.out.println(\"ad hero battle win\");\n> } \n> \n> public static void main(String[] args) {\n> Hero.battleWin(); // 调用父类的方法\n> ADHero.battleWin();//调用子类的方法\n> }\n> \n> }\n> ```\n>\n> 问:对于`Hero h =new ADHero();` \n>\n> h是父类类型的引用,但是指向一个子类对象h.battleWin(); 会调用父类的方法?还是子类的方法?\n>\n> 答:当父类的引用指向一个子类对象时,执行的:\n>\n> - 对象方法是子类的对象方法(因为重写)\n> - 类方法是父类的类方法(类方法不能被重写)\n\n## super关键字\n\n`super()`用于子类的构造函数内部;\n\n特点:\n\n1. 实例化一个父类的时候,父类的构造方法会被自动调用(根据实例化方式来选择调用有参或无参的构造方法);\n\n2. 实例化一个子类的时候,若没有写 `super()`语句,会默认调用父类无参构造方法。\n\n3. 并且,父类和子类的构造方法**都会**被调用,且父类的构造方法**先**被调用\n\n```java\npackage superKeyWord;\n//父类:\npublic class Hero{\n public Hero(){\n System.out.println(\"调用父类的构造方法\");\n }\n}\n```\n\n```java\npackage superKeyWord;\n\n//子类\npublic class SuperTest extends Hero {\n public SuperTest(){\n System.out.println(\"调用子类构造方法\");\n }\n\n public static void main(String[] args){\n new Hero();\t\t\t\t\t\t\t//创建父类对象\n System.out.println(\"---------------\");\n new SuperTest();\t\t\t\t\t//创建子类对象\n }\n \n}\n\n/*输出:\n调用父类的构造方法\n---------------\n调用父类的构造方法\n调用子类构造方法\n*/\n```\n\n### super调用父类带参构造方法\n\n`super() ` 相当于一个父类的对象,在子类中使用就类似于创建了一个父类的对象,会调用父类**带参**的构造方法\n\n`super` 与 `this` 类似,this是当前类的对象,super是父类的对象,两者都只能在方法内部使用\n\n```java\npackage superKeyWord;\n\n//父类:\npublic class Hero{\n public Hero(String name){ \t\t\t//带参的构造方法\n \n System.out.println(\"调用父类的构造方法\"+name);\n \n }\n}\n```\n\n```java\npackage superKeyWord;\n\npublic class SuperTest extends Hero {\n \n // super(\"h2\"); //super只能在方法内部使用\n \n public SuperTest(){\n \n super(\"h2\"); //正确,super在方法内使用\n \n System.out.println(\"调用子类构造方法\");\n }\n\n public static void main(String[] args){\n new Hero(\"h1\");\n System.out.println(\"---------------\");\n new SuperTest();\n }\n \n}\n\n/*输出:\n调用父类的构造方法h1\n---------------\n调用父类的构造方法h2\n调用子类构造方法\n*/\n```\n\n### super调用父类属性\n\n```java\n//父类\npackage superKeyWord;\n\npublic class Hero{\n\n int moveSpeed = 100;\n \n}\n```\n\n```java\n//子类\n\npackage superKeyWord;\n\npublic class SuperTest extends Hero {\n \n int moveSpeed = 200;\n \n public void getMoveSpeed1(){\n System.out.println(super.moveSpeed); //打印父类moveSpeed\n }\n\n public void getMoveSpeed2() {\n System.out.println(this.moveSpeed); //打印子类moveSpeed\n }\n\n public static void main(String[] args){\n \n SuperTest h = new SuperTest();\n h.getMoveSpeed1(); //输出:100\n h.getMoveSpeed2();\t\t\t\t//输出:200\n }\n \n}\n```\n\n### super调用父类方法\n\n当子类重写了父类的方法后,super调用的依然是父类原本的方法\n\n\n\n## Object类\n\nObject类是所有类的父类,即声明一个类的时候,默认就继承了Object\n\n\n\n### Object提供的一些方法\n\n- toString():\n\n 返回当前对象的**字符串表达**\n 通过 System.out.println()打印对象就是打印该对象的toString()返回值\n\n ```java\n Hero h = new Hero();\n \n //下面两行等效\n System.out.println(h);\n System.out.println(h.toString());\n \n ```\n\n- finalize():\n\n 当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件\n\n 当它被垃圾回收的时候,它的finalize() 方法就会被调用。\n\n finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用\n\n ```java\n Hero h;\n \n h = new Hero();\n h = new Hero();\n ```\n\n 执行第四行的时候,第三行的 `Hero()` 对象没了引用指向,就满足垃圾回收条件\n\n- equals():\n\n equals() 用于判断两个对象的**内容**是否相同,假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同\n\n ```java\n package charactor;\n \n public class Hero {\n public String name;\n protected float hp;\n \n public boolean equals(Object o){\n if(o instanceof Hero){\n Hero h = (Hero) o;\n return this.hp == h.hp;\n }\n return false;\n }\n \n public static void main(String[] args) {\n Hero h1= new Hero();\n h1.hp = 300;\n Hero h2= new Hero();\n h2.hp = 400;\n Hero h3= new Hero();\n h3.hp = 300;\n \n System.out.println(h1.equals(h2));\n System.out.println(h1.equals(h3));\n }\n }\n ```\n\n \n\n- `==`\n\n 这不是Object的方法,但是用于判断两个对象是否相同,\n **更准确的讲**,用于判断两个引用,是否指向了同一个对象\n\n- hashCode():\n\n 返回对象的哈希值\n\n- 线程同步方法\n\n ```java\n wait()\n notify()\n notifyAll()\n ```\n\n- getClass:\n\n 会返回一个对象的**类对象**\n\n\n\n## final修饰\n\nfinal修饰类,方法,基本类型变量,引用的时候分别有不同的意思。\n\n- 修饰类:表示该类不能被继承\n\n ```java\n public final class Hero{ //该类不能被继承\n \n }\n ```\n\n \n\n- 修饰方法:表示该方法不能被重写\n\n ```java\n public final void useItem(){\n \n } \n ```\n\n \n\n- 修饰基本变量:表示该变量只能被赋值一次\n\n ```java\n final int a;\n a = 1;\n a = 2; //报错\n ```\n\n \n\n- 修饰引用:表示该引用只有一次指向对象的机会\n\n ```java\n final Hero h;\n h = new Hero();\n h = new Hero(); //报错\n ```\n\n> 可以用 `final` 修饰变量使其作为常量使用\n>\n> 常量指的是可以公开,直接访问,不会变化的值\n\n\n\n## 抽象类\n\n### 抽象类\n\n在类中声明一个方法,这个方法没有实现体,是一个“空”方法\n\n这样的方法就叫抽象方法,使用修饰符`abstract`\n\n> 注意:\n>\n> - 当一个类有抽象方法的时候,该类必须被声明为抽象类\n>\n> - 子类继承抽象类后,必须重写抽象方法(如果有的话),赋予其具体功能\n> - 抽象类可以没有抽象方法,可以有实体方法,但是有抽象方法时必须声明为抽象类\n> - 抽象类不能直接进行实例化,即不能创建抽象类的对象\n\n```java\npackage charactor;\n \npublic abstract class Hero { //抽象类\n \n String name;\n float hp;\n float armor;\n int moveSpeed;\n \n public abstract void attack();// 抽象方法attack\n \n}\n```\n\n### 抽象类与接口区别\n\n1. 接口的方法默认是 `public`,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。\n2. 接口中除了 `static`、`final` 变量,不能有其他变量,而抽象类中则不一定。\n3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 `extends` 关键字扩展多个接口。\n4. 接口方法默认修饰符是 `public`,抽象方法可以有 `public`、`protected` 和 `default` 这些修饰符(抽象方法就是为了被重写所以不能使用 `private` 关键字修饰!)。\n5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。\n\n| 抽象类 | 接口 |\n| :------------------------------------: | :------------------: |\n| 抽象类也是类,子类只能继承一个类 | 子类可以实现多个接口 |\n| 可以是public,protected,package,private | 只能是public |\n| 静态、非静态 | 静态 |\n| final或非final的属性 | final的 |\n\n> 另外,抽象类和接口都可以有实体方法。 接口中的实体方法,叫做[默认方法](#14.11 默认方法)\n\n## 内部类\n\n内部类分为四种:\n\n- 非静态内部类\n\n 非静态内部类,只有一个外部类对象存在的时候,才有意义。也就是说,要调用非静态内部类的属性和方法,必须要先创建一个外部类\n\n ```java\n public class Hero{\n String name;\n \n class BattleScore{ //内部类\n int score;\n public void kill(){\n \n }\n }\n }\n ```\n\n 调用内部类的途径:\n\n ```java\n Hero h = new Hero(); //先创建外部类\n BattleScore bs = h.new BattleScore();\n ```\n\n \n\n- 静态内部类\n\n 在一个类里面声明一个静态内部类,静态内部类的实例化**不需要**一个外部类的实例为基础,可以直接实例化。\n\n 另外,静态内部类不能直接访问外部类的对象属性\n\n 语法:\n\n ```java\n new 外部类.静态内部类();\n ```\n\n 例:\n\n ```java\n public class Hero{\n String name;\n \n static class BattleScore{ //静态内部类\n int score;\n public void kill(){\n \n //静态内部类不能直接访问外部类的对象属性\n System.out.println(name + \"kille a Hero\");//报错\n }\n }\n }\n ```\n\n 实例化:\n\n ```java\n Hero.BattleScore bs = new Hero.BattleScore();\n ```\n\n \n\n- 匿名类\n\n 匿名类指的是在**创建某个类的对象的同时实例化它**,使代码更加简洁精练\n 通常情况下,要使用一个接口或者抽象类,都必须创建一个子类,为了快速使用,直接实例化一个抽象类,并“**当场**”实现其抽象方法。\n 既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做**匿名类**\n\n ```java\n package charactor;\n \n public abstract class Hero {\n String name; \n float hp; \n float armor; \n int moveSpeed; \n \n public abstract void attack(); //抽象方法\n \n public static void main(String[] args) {\n \n Hero h = new Hero(){\n //当场实现attack方法\n public void attack() {\n System.out.println(\"新的进攻手段\");\n }\n };\n h.attack();\n //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名\n \n System.out.println(h);\n }\n \n }\n ```\n\n 注意:在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final,但在jdk8中,已经不需要强制修饰成final了,因为编译器**偷偷的**帮你加上了看不见的final\n\n- 本地类\n\n 本地类可以理解为有名字的匿名类\n\n - **内部类**与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。\n - **本地类**和匿名类**一样**,直接声明在代码块里面,可以是主方法,for循环里等等地方\n\n ```java\n package charactor;\n \n public abstract class Hero {\n String name; \n float hp; \n float armor; \n int moveSpeed; \n \n public abstract void attack();\n \n public static void main(String[] args) {\n \n //在主方法里声明本地类\n //与匿名类的区别在于,本地类有了自定义的类名\n class SomeHero extends Hero{\n public void attack() {\n System.out.println( name+ \" 新的进攻手段\");\n }\n }\n \n SomeHero h =new SomeHero();\n h.name =\"地卜师\";\n h.attack();\n }\n \n }\n ```\n\n\n\n## 默认方法\n\n默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法\n\n例如:\n\n```java\npublic interface Mortal {\n public void die(); //抽象方法\n \n default public void revive() { //具体方法\n \n System.out.println(\"本英雄复活了\");\n }\n}\n```\n\n> 为什么会有默认方法?\n>\n> 假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的子类,都需要做改动(都要在子类中考虑重写revive的具体方法)。\n>\n> 但是引入了默认方法后,原来的类,不需要做任何改动,并且还能**得到**这个默认方法\n>\n> 通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类\n\n","source":"_posts/javaSE/14-接口与继承.md","raw":"---\ntitle: 14-接口与继承\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: ab09b476\ndate: 2021-02-14 15:38:23\ndescription: 内容较多,主要是关于类的一些知识:接口和抽象类、子类与父类的关系、内部类等\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n# 接口与继承\n\n## 接口\n\n### 接口\n\n接口的创建:(与创建类相似)\n\n```java\n//创建一个播放器接口\npublic interface Player{\n public void play();\n public void pause();\n public void stop();\n public void tune(); //接口只有方法声明,没有主体,并且默认都是public的,可以不写“public”\n}\n```\n\nJava接口是一系列方法的**声明**(没有主体),是一些方法特征的集合;\n\n一个接口只有方法的特征**没有方法的实现**(不能使用 new来创建实例),因此这些方法可以在不同的地方被不同的类实现(继承),进而在不同的类中实现不同的功能。\n\n接口就是一组抽象方法和常量值的集合。可以把接口看成是一种特殊的抽象类。\n\n(1)其所有的方法都必须是抽象的(abstract)。\n\n(2)其属性成员(若有)只能是final static的常量。\n\n### 实现接口\n\n实现一个接口与类的继承相似,用 `implements` 来实现\n\n```java\n// 创建一个MP3类,实现播放器功能\npublic MP3 implements Player{\n public void play(){\n System.out.println(\"播放\");\n }\n \n public void pause(){\n System.out.println(\"暂停\");\n }\n \n public void stop(){\n System.out.println(\"停止\");\n }\n \n public void tune(){\n System.out.println(\"调节音量\");\n }\n}\n```\n\n\n\n## 对象转型\n\n### 明确引用类型与对象类型的概念\n\n引用和对象都是有类型的\n\n```java\nHero h = new Hero();\n```\n\n在这个例子,引用是 `h`,对象是 `new Hero()`,它们的类型均为 `Hero`\n\n通常情况引用类型与对象类型是一样的,但也有不一样的时候\n\n### 子类转父类(向上转型)\n\n所谓的转型,是指当**引用类型**和**对象类型**不一致的时候,才需要进行类型转换;\n类型转换有时候会成功,有时候会失败\n\n假如 `ADHero` 是 `Hero` 的子类\n\n```java\nHero h = new Hero();\nADHero ad = new ADHero();\n\n```\n\n用图来表示其关系如下:\n\n\"image-20210126224549229\"\n\n```java\nh = ad; //向上转型\n```\n\n转型后的图:\n\n\"image-20210126224655825\"\n\n可以看到 `h` 经过 `ad` 最终指向的还是 `Hero`\n\n\n\n因此,把 `h` 指向 `ADHero` 一定可以,因为 `ADHero` 继承了 `Hero`,Hero能实现的功能 ADHero 也可以\n\n\n\n### 父类转子类(向下转型)\n\n父类转子类,有的时候行,**有的时候不行**,所以必须进行强制转换。\n强制转换的意思就是:转换有风险,风险自担。\n\n假如 `ADHero` 和 `Support` 是 `Hero` 的两个不同子类:\n\n```java\nHero h =new Hero();\nADHero ad = new ADHero();\nSupport s = new Support(); \n```\n\n```java\nh = s; // 向上转型,一定可以\n```\n\n向上转型一定可以,因为 `h`通过 `s` 最终还是指向 `Hero`\n\n\"image-20210126225703371\"\n\n\n\n```java\nad = (ADHero)h; //向下转型,不一定可以,需要强制转换\n```\n\n虽然 `ad` 指向的 `ADHero` 是 `Hero` 的子类,眨眼看来 `ad` 最终也是指向 Hero 的,但实际上指向的是 ADHero,ADHero与Hero还是有区别的,ADHero有的方法Hero不一定有\n\n而ad通过h最终指向的是 Hero,而不是 ADHero,因此会有风险(指向ADHero才不会有风险),只能强制转换。\n\n\"image-20210126230408061\"\n\n因为子类拥有的方法父类不一定有,因此 `h` 向下转型后,`ad`可能就没有了一些子类的方法\n\n### 没有继承关系的两个类\n\n没有继承关系的两个类,互相转换,一定会失败\n\n\n\n### 实现类转换成接口(向上转型)\n\n假如 `AD` 是一个接口,`ADHero` 类继承了它\n\n```java\npublic class Test {\n public static void main(String[] args) {\n ADHero ad = new ADHero();\n \n AD adi = ad; //向上转型 \n } \n}\n```\n\n\n\n### 转型练习\n\n如下转换能否成功?如果不能,是哪一行会出错?为什么会出错?\n\n```java\npublic class Hero {\n public String name;\n protected float hp;\n \n public static void main(String[] args) {\n ADHero ad = new ADHero();\n Hero h = ad;\n AD adi = (AD) h;\n APHero ap = (APHero) adi;\n }\n}\n```\n\n\n\n| 分析 |\n| :----------------------------------------------------------- |\n| 第7行向上转型没问题 |\n| 第8行,可以将h强制转换为AD,因为 AD 与 Hero 之间通过子类关联在了一起;因此 h 指向了AD,而通过 h -> ad -> ADHero -> AD,最终也是指向AD,虽有风险,但不会报错; |\n| 第9行,不能将 adi 强制转换为 APHero,因为 AD 与 APHero之间没有关联,因此ap本应该指向 `APHero`,但通过转换 ap -> adi -> AD,即最终指向了AD,因此不能进行转换,会报错 |\n\n\n\n\n\n\n\n\"image-20210126234924701\"\n\n\n\n### instanceof 语句\n\n`instanceof Hero` 判断一个引用所指向的对象,是否是以下两种,返回 true 或 false\n\n- Hero类型\n- Hero的子类\n\n```java\npackage charactor;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public static void main(String[] args) {\n ADHero ad = new ADHero();\n APHero ap = new APHero();\n \n Hero h1= ad;\n Hero h2= ap;\n \n //判断引用h1指向的对象,是否是ADHero类型\n System.out.println(h1 instanceof ADHero); \t//true\n \n //判断引用h2指向的对象,是否是APHero类型\n System.out.println(h2 instanceof APHero);\t//true\n \n //判断引用h1指向的对象,是否是Hero的子类型\n System.out.println(h1 instanceof Hero);\t\t//true\n }\n}\n```\n\n\n\n## 重写\n\n子类可以继承父类的对象方法\n\n在继承后,重复提供该方法,就叫做方法的重写\n\n又叫**覆盖** override\n\n> 父类Item有一个方法,叫做effect\n>\n> ```java\n> package property;\n> \n> public class Item {\n> String name;\n> int price;\n> \n> public void buy(){\n> System.out.println(\"购买\");\n> }\n> public void effect() {\n> System.out.println(\"物品使用后,可以有效果\");\n> }\n> \n> }\n> ```\n>\n> 子类LifePotion继承Item,同时也提供了方法effect\n>\n> ```java\n> package property;\n> \n> public class LifePotion extends Item{\n> \n> public void effect(){ \t\t\t\t\t\t//重写effect()方法\n> System.out.println(\"血瓶使用后,可以回血\");\n> }\n> \n> }\n> ```\n>\n> 调用重写的方法\n> 调用就会执行重写的方法,而不是从父类的方法\n\n重写的优点:\n\n如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。\n\n但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能**放弃继承Item**,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动.\n\n这样就增加了开发时间和维护成本\n\n\n\n## 多态\n\n### 操作符的多态\n\n同一个操作符在不同情境下,具备不同的作用:\n\n- 如果+号两侧都是整型,那么`+`代表 数字相加\n- 如果+号两侧,**任意**一个是字符串,那么`+`代表字符串连接\n\n### 类的多态\n\n多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态(实际是子类重写了父类的方法,导致子类的方法与父类的方法不同,子类之间的方法也不同)\n\n**类的多态**即父类引用指向不同的子类对象,调用同一个方法时出现不同的效果\n\n> 假设 Hero是 `ADHero` 与 `APHero` 的父类\n>\n> ```java\n> Hero h1 = new ADHero();\n> Hero h2 = new APHero();\n> ```\n\n类的多态的条件:\n\n1. 父类(接口)引用指向子类对象\n\n2. 调用的方法有[重写](#14.3 重写)\n\n### 使用类多态的好处\n\n如果不使用多态,例如:\n\n> 假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法\n> useLifePotion、useMagicPotion;\n> 除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如\n> usePurityPotion、useGuard、useInvisiblePotion等等等等\n\n```java\npackage charactor;\n \nimport property.LifePotion;\nimport property.MagicPotion;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void useLifePotion(LifePotion lp){\n lp.effect();\n }\n public void useMagicPotion(MagicPotion mp){\n mp.effect();\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n \n LifePotion lp =new LifePotion();\n MagicPotion mp =new MagicPotion();\n \n garen.useLifePotion(lp);\n garen.useMagicPotion(mp);\n \n }\n \n}\n```\n\n这个时候采用**多态**来解决这个问题:\n\n> 设计一个方法叫做useItem,其参数类型是Item\n>\n> - 如果是使用血瓶,调用该方法\n> - 如果是使用魔瓶,还是调用该方法\n> - 无论英雄要使用什么样的物品,**只需要一个方法** 即可\n\n```java\npackage charactor;\n \nimport property.Item;\nimport property.LifePotion;\nimport property.MagicPotion;\n \npublic class Hero {\n public String name;\n protected float hp;\n \n public void useItem(Item i){ //设计一个方法叫做useItem,其参数类型是Item\n i.effect();\n }\n \n public static void main(String[] args) {\n \n Hero garen = new Hero();\n garen.name = \"盖伦\";\n \n LifePotion lp =new LifePotion();\n MagicPotion mp =new MagicPotion();\n \n garen.useItem(lp); //使用血瓶lp\n garen.useItem(mp); //使用魔瓶mp\n \n }\n \n}\n```\n\n\n\n## 隐藏\n\n与重写类似:\n\n- **重写,是**子类覆盖父类的**对象方法**;\n- **隐藏**,就是子类覆盖父类的**类方法**\n\n如何隐藏呢:\n\n> 父类有一个类方法 :battleWin\n>\n> ```java\n> package charactor;\n> \n> public class Hero {\n> public String name;\n> protected float hp;\n> \n> public static void battleWin(){ //类方法,静态方法\n> System.out.println(\"hero battle win\");\n> }\n> \n> }\n> ```\n>\n> 创建一个子类,隐藏父类方法 `battleWin()`\n>\n> ```java\n> package charactor;\n> \n> public class ADHero extends Hero implements AD{\n> \n> //隐藏父类的battleWin方法\n> public static void battleWin(){\n> System.out.println(\"ad hero battle win\");\n> } \n> \n> public static void main(String[] args) {\n> Hero.battleWin(); // 调用父类的方法\n> ADHero.battleWin();//调用子类的方法\n> }\n> \n> }\n> ```\n>\n> 问:对于`Hero h =new ADHero();` \n>\n> h是父类类型的引用,但是指向一个子类对象h.battleWin(); 会调用父类的方法?还是子类的方法?\n>\n> 答:当父类的引用指向一个子类对象时,执行的:\n>\n> - 对象方法是子类的对象方法(因为重写)\n> - 类方法是父类的类方法(类方法不能被重写)\n\n## super关键字\n\n`super()`用于子类的构造函数内部;\n\n特点:\n\n1. 实例化一个父类的时候,父类的构造方法会被自动调用(根据实例化方式来选择调用有参或无参的构造方法);\n\n2. 实例化一个子类的时候,若没有写 `super()`语句,会默认调用父类无参构造方法。\n\n3. 并且,父类和子类的构造方法**都会**被调用,且父类的构造方法**先**被调用\n\n```java\npackage superKeyWord;\n//父类:\npublic class Hero{\n public Hero(){\n System.out.println(\"调用父类的构造方法\");\n }\n}\n```\n\n```java\npackage superKeyWord;\n\n//子类\npublic class SuperTest extends Hero {\n public SuperTest(){\n System.out.println(\"调用子类构造方法\");\n }\n\n public static void main(String[] args){\n new Hero();\t\t\t\t\t\t\t//创建父类对象\n System.out.println(\"---------------\");\n new SuperTest();\t\t\t\t\t//创建子类对象\n }\n \n}\n\n/*输出:\n调用父类的构造方法\n---------------\n调用父类的构造方法\n调用子类构造方法\n*/\n```\n\n### super调用父类带参构造方法\n\n`super() ` 相当于一个父类的对象,在子类中使用就类似于创建了一个父类的对象,会调用父类**带参**的构造方法\n\n`super` 与 `this` 类似,this是当前类的对象,super是父类的对象,两者都只能在方法内部使用\n\n```java\npackage superKeyWord;\n\n//父类:\npublic class Hero{\n public Hero(String name){ \t\t\t//带参的构造方法\n \n System.out.println(\"调用父类的构造方法\"+name);\n \n }\n}\n```\n\n```java\npackage superKeyWord;\n\npublic class SuperTest extends Hero {\n \n // super(\"h2\"); //super只能在方法内部使用\n \n public SuperTest(){\n \n super(\"h2\"); //正确,super在方法内使用\n \n System.out.println(\"调用子类构造方法\");\n }\n\n public static void main(String[] args){\n new Hero(\"h1\");\n System.out.println(\"---------------\");\n new SuperTest();\n }\n \n}\n\n/*输出:\n调用父类的构造方法h1\n---------------\n调用父类的构造方法h2\n调用子类构造方法\n*/\n```\n\n### super调用父类属性\n\n```java\n//父类\npackage superKeyWord;\n\npublic class Hero{\n\n int moveSpeed = 100;\n \n}\n```\n\n```java\n//子类\n\npackage superKeyWord;\n\npublic class SuperTest extends Hero {\n \n int moveSpeed = 200;\n \n public void getMoveSpeed1(){\n System.out.println(super.moveSpeed); //打印父类moveSpeed\n }\n\n public void getMoveSpeed2() {\n System.out.println(this.moveSpeed); //打印子类moveSpeed\n }\n\n public static void main(String[] args){\n \n SuperTest h = new SuperTest();\n h.getMoveSpeed1(); //输出:100\n h.getMoveSpeed2();\t\t\t\t//输出:200\n }\n \n}\n```\n\n### super调用父类方法\n\n当子类重写了父类的方法后,super调用的依然是父类原本的方法\n\n\n\n## Object类\n\nObject类是所有类的父类,即声明一个类的时候,默认就继承了Object\n\n\n\n### Object提供的一些方法\n\n- toString():\n\n 返回当前对象的**字符串表达**\n 通过 System.out.println()打印对象就是打印该对象的toString()返回值\n\n ```java\n Hero h = new Hero();\n \n //下面两行等效\n System.out.println(h);\n System.out.println(h.toString());\n \n ```\n\n- finalize():\n\n 当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件\n\n 当它被垃圾回收的时候,它的finalize() 方法就会被调用。\n\n finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用\n\n ```java\n Hero h;\n \n h = new Hero();\n h = new Hero();\n ```\n\n 执行第四行的时候,第三行的 `Hero()` 对象没了引用指向,就满足垃圾回收条件\n\n- equals():\n\n equals() 用于判断两个对象的**内容**是否相同,假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同\n\n ```java\n package charactor;\n \n public class Hero {\n public String name;\n protected float hp;\n \n public boolean equals(Object o){\n if(o instanceof Hero){\n Hero h = (Hero) o;\n return this.hp == h.hp;\n }\n return false;\n }\n \n public static void main(String[] args) {\n Hero h1= new Hero();\n h1.hp = 300;\n Hero h2= new Hero();\n h2.hp = 400;\n Hero h3= new Hero();\n h3.hp = 300;\n \n System.out.println(h1.equals(h2));\n System.out.println(h1.equals(h3));\n }\n }\n ```\n\n \n\n- `==`\n\n 这不是Object的方法,但是用于判断两个对象是否相同,\n **更准确的讲**,用于判断两个引用,是否指向了同一个对象\n\n- hashCode():\n\n 返回对象的哈希值\n\n- 线程同步方法\n\n ```java\n wait()\n notify()\n notifyAll()\n ```\n\n- getClass:\n\n 会返回一个对象的**类对象**\n\n\n\n## final修饰\n\nfinal修饰类,方法,基本类型变量,引用的时候分别有不同的意思。\n\n- 修饰类:表示该类不能被继承\n\n ```java\n public final class Hero{ //该类不能被继承\n \n }\n ```\n\n \n\n- 修饰方法:表示该方法不能被重写\n\n ```java\n public final void useItem(){\n \n } \n ```\n\n \n\n- 修饰基本变量:表示该变量只能被赋值一次\n\n ```java\n final int a;\n a = 1;\n a = 2; //报错\n ```\n\n \n\n- 修饰引用:表示该引用只有一次指向对象的机会\n\n ```java\n final Hero h;\n h = new Hero();\n h = new Hero(); //报错\n ```\n\n> 可以用 `final` 修饰变量使其作为常量使用\n>\n> 常量指的是可以公开,直接访问,不会变化的值\n\n\n\n## 抽象类\n\n### 抽象类\n\n在类中声明一个方法,这个方法没有实现体,是一个“空”方法\n\n这样的方法就叫抽象方法,使用修饰符`abstract`\n\n> 注意:\n>\n> - 当一个类有抽象方法的时候,该类必须被声明为抽象类\n>\n> - 子类继承抽象类后,必须重写抽象方法(如果有的话),赋予其具体功能\n> - 抽象类可以没有抽象方法,可以有实体方法,但是有抽象方法时必须声明为抽象类\n> - 抽象类不能直接进行实例化,即不能创建抽象类的对象\n\n```java\npackage charactor;\n \npublic abstract class Hero { //抽象类\n \n String name;\n float hp;\n float armor;\n int moveSpeed;\n \n public abstract void attack();// 抽象方法attack\n \n}\n```\n\n### 抽象类与接口区别\n\n1. 接口的方法默认是 `public`,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。\n2. 接口中除了 `static`、`final` 变量,不能有其他变量,而抽象类中则不一定。\n3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 `extends` 关键字扩展多个接口。\n4. 接口方法默认修饰符是 `public`,抽象方法可以有 `public`、`protected` 和 `default` 这些修饰符(抽象方法就是为了被重写所以不能使用 `private` 关键字修饰!)。\n5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。\n\n| 抽象类 | 接口 |\n| :------------------------------------: | :------------------: |\n| 抽象类也是类,子类只能继承一个类 | 子类可以实现多个接口 |\n| 可以是public,protected,package,private | 只能是public |\n| 静态、非静态 | 静态 |\n| final或非final的属性 | final的 |\n\n> 另外,抽象类和接口都可以有实体方法。 接口中的实体方法,叫做[默认方法](#14.11 默认方法)\n\n## 内部类\n\n内部类分为四种:\n\n- 非静态内部类\n\n 非静态内部类,只有一个外部类对象存在的时候,才有意义。也就是说,要调用非静态内部类的属性和方法,必须要先创建一个外部类\n\n ```java\n public class Hero{\n String name;\n \n class BattleScore{ //内部类\n int score;\n public void kill(){\n \n }\n }\n }\n ```\n\n 调用内部类的途径:\n\n ```java\n Hero h = new Hero(); //先创建外部类\n BattleScore bs = h.new BattleScore();\n ```\n\n \n\n- 静态内部类\n\n 在一个类里面声明一个静态内部类,静态内部类的实例化**不需要**一个外部类的实例为基础,可以直接实例化。\n\n 另外,静态内部类不能直接访问外部类的对象属性\n\n 语法:\n\n ```java\n new 外部类.静态内部类();\n ```\n\n 例:\n\n ```java\n public class Hero{\n String name;\n \n static class BattleScore{ //静态内部类\n int score;\n public void kill(){\n \n //静态内部类不能直接访问外部类的对象属性\n System.out.println(name + \"kille a Hero\");//报错\n }\n }\n }\n ```\n\n 实例化:\n\n ```java\n Hero.BattleScore bs = new Hero.BattleScore();\n ```\n\n \n\n- 匿名类\n\n 匿名类指的是在**创建某个类的对象的同时实例化它**,使代码更加简洁精练\n 通常情况下,要使用一个接口或者抽象类,都必须创建一个子类,为了快速使用,直接实例化一个抽象类,并“**当场**”实现其抽象方法。\n 既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做**匿名类**\n\n ```java\n package charactor;\n \n public abstract class Hero {\n String name; \n float hp; \n float armor; \n int moveSpeed; \n \n public abstract void attack(); //抽象方法\n \n public static void main(String[] args) {\n \n Hero h = new Hero(){\n //当场实现attack方法\n public void attack() {\n System.out.println(\"新的进攻手段\");\n }\n };\n h.attack();\n //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名\n \n System.out.println(h);\n }\n \n }\n ```\n\n 注意:在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final,但在jdk8中,已经不需要强制修饰成final了,因为编译器**偷偷的**帮你加上了看不见的final\n\n- 本地类\n\n 本地类可以理解为有名字的匿名类\n\n - **内部类**与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。\n - **本地类**和匿名类**一样**,直接声明在代码块里面,可以是主方法,for循环里等等地方\n\n ```java\n package charactor;\n \n public abstract class Hero {\n String name; \n float hp; \n float armor; \n int moveSpeed; \n \n public abstract void attack();\n \n public static void main(String[] args) {\n \n //在主方法里声明本地类\n //与匿名类的区别在于,本地类有了自定义的类名\n class SomeHero extends Hero{\n public void attack() {\n System.out.println( name+ \" 新的进攻手段\");\n }\n }\n \n SomeHero h =new SomeHero();\n h.name =\"地卜师\";\n h.attack();\n }\n \n }\n ```\n\n\n\n## 默认方法\n\n默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法\n\n例如:\n\n```java\npublic interface Mortal {\n public void die(); //抽象方法\n \n default public void revive() { //具体方法\n \n System.out.println(\"本英雄复活了\");\n }\n}\n```\n\n> 为什么会有默认方法?\n>\n> 假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的子类,都需要做改动(都要在子类中考虑重写revive的具体方法)。\n>\n> 但是引入了默认方法后,原来的类,不需要做任何改动,并且还能**得到**这个默认方法\n>\n> 通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类\n\n","slug":"javaSE/14-接口与继承","published":1,"updated":"2021-08-29T15:31:35.517Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6tw00h8akveg72k4lri","content":"

接口与继承

接口

接口

接口的创建:(与创建类相似)

\n
1
2
3
4
5
6
7
//创建一个播放器接口
public interface Player{
public void play();
public void pause();
public void stop();
public void tune(); //接口只有方法声明,没有主体,并且默认都是public的,可以不写“public”
}
\n

Java接口是一系列方法的声明(没有主体),是一些方法特征的集合;

\n

一个接口只有方法的特征没有方法的实现(不能使用 new来创建实例),因此这些方法可以在不同的地方被不同的类实现(继承),进而在不同的类中实现不同的功能。

\n

接口就是一组抽象方法和常量值的集合。可以把接口看成是一种特殊的抽象类。

\n

(1)其所有的方法都必须是抽象的(abstract)。

\n

(2)其属性成员(若有)只能是final static的常量。

\n

实现接口

实现一个接口与类的继承相似,用 implements 来实现

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建一个MP3类,实现播放器功能
public MP3 implements Player{
public void play(){
System.out.println("播放");
}

public void pause(){
System.out.println("暂停");
}

public void stop(){
System.out.println("停止");
}

public void tune(){
System.out.println("调节音量");
}
}
\n

对象转型

明确引用类型与对象类型的概念

引用和对象都是有类型的

\n
1
Hero h = new Hero();
\n

在这个例子,引用是 h,对象是 new Hero(),它们的类型均为 Hero

\n

通常情况引用类型与对象类型是一样的,但也有不一样的时候

\n

子类转父类(向上转型)

所谓的转型,是指当引用类型对象类型不一致的时候,才需要进行类型转换;
类型转换有时候会成功,有时候会失败

\n

假如 ADHeroHero 的子类

\n
1
2
3
Hero h = new Hero();
ADHero ad = new ADHero();

\n

用图来表示其关系如下:

\n

\"image-20210126224549229\"

\n
1
h = ad;                      //向上转型
\n

转型后的图:

\n

\"image-20210126224655825\"

\n

可以看到 h 经过 ad 最终指向的还是 Hero

\n

因此,把 h 指向 ADHero 一定可以,因为 ADHero 继承了 Hero,Hero能实现的功能 ADHero 也可以

\n

父类转子类(向下转型)

父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。
强制转换的意思就是:转换有风险,风险自担。

\n

假如 ADHeroSupportHero 的两个不同子类:

\n
1
2
3
Hero h =new Hero();
ADHero ad = new ADHero();
Support s = new Support();
\n
1
h = s;            // 向上转型,一定可以
\n

向上转型一定可以,因为 h通过 s 最终还是指向 Hero

\n

\"image-20210126225703371\"

\n
1
ad = (ADHero)h;    //向下转型,不一定可以,需要强制转换
\n

虽然 ad 指向的 ADHeroHero 的子类,眨眼看来 ad 最终也是指向 Hero 的,但实际上指向的是 ADHero,ADHero与Hero还是有区别的,ADHero有的方法Hero不一定有

\n

而ad通过h最终指向的是 Hero,而不是 ADHero,因此会有风险(指向ADHero才不会有风险),只能强制转换。

\n

\"image-20210126230408061\"

\n

因为子类拥有的方法父类不一定有,因此 h 向下转型后,ad可能就没有了一些子类的方法

\n

没有继承关系的两个类

没有继承关系的两个类,互相转换,一定会失败

\n

实现类转换成接口(向上转型)

假如 AD 是一个接口,ADHero 类继承了它

\n
1
2
3
4
5
6
7
public class Test {
public static void main(String[] args) {
ADHero ad = new ADHero();

AD adi = ad; //向上转型
}
}
\n

转型练习

如下转换能否成功?如果不能,是哪一行会出错?为什么会出错?

\n
1
2
3
4
5
6
7
8
9
10
11
public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {
ADHero ad = new ADHero();
Hero h = ad;
AD adi = (AD) h;
APHero ap = (APHero) adi;
}
}
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
分析
第7行向上转型没问题
第8行,可以将h强制转换为AD,因为 AD 与 Hero 之间通过子类关联在了一起;因此 h 指向了AD,而通过 h -> ad -> ADHero -> AD,最终也是指向AD,虽有风险,但不会报错;
第9行,不能将 adi 强制转换为 APHero,因为 AD 与 APHero之间没有关联,因此ap本应该指向 APHero,但通过转换 ap -> adi -> AD,即最终指向了AD,因此不能进行转换,会报错
\n
\n

\"image-20210126234924701\"

\n

instanceof 语句

instanceof Hero 判断一个引用所指向的对象,是否是以下两种,返回 true 或 false

\n
    \n
  • Hero类型
  • \n
  • Hero的子类
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package charactor;

public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {
ADHero ad = new ADHero();
APHero ap = new APHero();

Hero h1= ad;
Hero h2= ap;

//判断引用h1指向的对象,是否是ADHero类型
System.out.println(h1 instanceof ADHero); \t//true

//判断引用h2指向的对象,是否是APHero类型
System.out.println(h2 instanceof APHero);\t//true

//判断引用h1指向的对象,是否是Hero的子类型
System.out.println(h1 instanceof Hero);\t\t//true
}
}
\n

重写

子类可以继承父类的对象方法

\n

在继承后,重复提供该方法,就叫做方法的重写

\n

又叫覆盖 override

\n
\n

父类Item有一个方法,叫做effect

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package property;

public class Item {
String name;
int price;

public void buy(){
System.out.println("购买");
}
public void effect() {
System.out.println("物品使用后,可以有效果");
}

}
\n

子类LifePotion继承Item,同时也提供了方法effect

\n
1
2
3
4
5
6
7
8
9
package property;

public class LifePotion extends Item{

public void effect(){ \t\t\t\t\t\t//重写effect()方法
System.out.println("血瓶使用后,可以回血");
}

}
\n

调用重写的方法
调用就会执行重写的方法,而不是从父类的方法

\n
\n

重写的优点:

\n

如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。

\n

但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能放弃继承Item,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动.

\n

这样就增加了开发时间和维护成本

\n

多态

操作符的多态

同一个操作符在不同情境下,具备不同的作用:

\n
    \n
  • 如果+号两侧都是整型,那么+代表 数字相加
  • \n
  • 如果+号两侧,任意一个是字符串,那么+代表字符串连接
  • \n
\n

类的多态

多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态(实际是子类重写了父类的方法,导致子类的方法与父类的方法不同,子类之间的方法也不同)

\n

类的多态即父类引用指向不同的子类对象,调用同一个方法时出现不同的效果

\n
\n

假设 Hero是 ADHeroAPHero 的父类

\n
1
2
Hero h1 = new ADHero();
Hero h2 = new APHero();
\n
\n

类的多态的条件:

\n
    \n
  1. 父类(接口)引用指向子类对象

    \n
  2. \n
  3. 调用的方法有重写

    \n
  4. \n
\n

使用类多态的好处

如果不使用多态,例如:

\n
\n

假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法
useLifePotion、useMagicPotion;
除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如
usePurityPotion、useGuard、useInvisiblePotion等等等等

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package charactor;

import property.LifePotion;
import property.MagicPotion;

public class Hero {
public String name;
protected float hp;

public void useLifePotion(LifePotion lp){
lp.effect();
}
public void useMagicPotion(MagicPotion mp){
mp.effect();
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";

LifePotion lp =new LifePotion();
MagicPotion mp =new MagicPotion();

garen.useLifePotion(lp);
garen.useMagicPotion(mp);

}

}
\n

这个时候采用多态来解决这个问题:

\n
\n

设计一个方法叫做useItem,其参数类型是Item

\n
    \n
  • 如果是使用血瓶,调用该方法
  • \n
  • 如果是使用魔瓶,还是调用该方法
  • \n
  • 无论英雄要使用什么样的物品,只需要一个方法 即可
  • \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package charactor;

import property.Item;
import property.LifePotion;
import property.MagicPotion;

public class Hero {
public String name;
protected float hp;

public void useItem(Item i){ //设计一个方法叫做useItem,其参数类型是Item
i.effect();
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";

LifePotion lp =new LifePotion();
MagicPotion mp =new MagicPotion();

garen.useItem(lp); //使用血瓶lp
garen.useItem(mp); //使用魔瓶mp

}

}
\n

隐藏

与重写类似:

\n
    \n
  • 重写,是子类覆盖父类的对象方法
  • \n
  • 隐藏,就是子类覆盖父类的类方法
  • \n
\n

如何隐藏呢:

\n
\n

父类有一个类方法 :battleWin

\n
1
2
3
4
5
6
7
8
9
10
11
package charactor;

public class Hero {
public String name;
protected float hp;

public static void battleWin(){ //类方法,静态方法
System.out.println("hero battle win");
}

}
\n

创建一个子类,隐藏父类方法 battleWin()

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package charactor;

public class ADHero extends Hero implements AD{

//隐藏父类的battleWin方法
public static void battleWin(){
System.out.println("ad hero battle win");
}

public static void main(String[] args) {
Hero.battleWin(); // 调用父类的方法
ADHero.battleWin();//调用子类的方法
}

}
\n

问:对于Hero h =new ADHero();

\n

h是父类类型的引用,但是指向一个子类对象h.battleWin(); 会调用父类的方法?还是子类的方法?

\n

答:当父类的引用指向一个子类对象时,执行的:

\n
    \n
  • 对象方法是子类的对象方法(因为重写)
  • \n
  • 类方法是父类的类方法(类方法不能被重写)
  • \n
\n
\n

super关键字

super()用于子类的构造函数内部;

\n

特点:

\n
    \n
  1. 实例化一个父类的时候,父类的构造方法会被自动调用(根据实例化方式来选择调用有参或无参的构造方法);

    \n
  2. \n
  3. 实例化一个子类的时候,若没有写 super()语句,会默认调用父类无参构造方法。

    \n
  4. \n
  5. 并且,父类和子类的构造方法都会被调用,且父类的构造方法被调用

    \n
  6. \n
\n
1
2
3
4
5
6
7
package superKeyWord;
//父类:
public class Hero{
public Hero(){
System.out.println("调用父类的构造方法");
}
}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package superKeyWord;

//子类
public class SuperTest extends Hero {
public SuperTest(){
System.out.println("调用子类构造方法");
}

public static void main(String[] args){
new Hero();\t\t\t\t\t\t\t//创建父类对象
System.out.println("---------------");
new SuperTest();\t\t\t\t\t//创建子类对象
}

}

/*输出:
调用父类的构造方法
---------------
调用父类的构造方法
调用子类构造方法
*/
\n

super调用父类带参构造方法

super() 相当于一个父类的对象,在子类中使用就类似于创建了一个父类的对象,会调用父类带参的构造方法

\n

superthis 类似,this是当前类的对象,super是父类的对象,两者都只能在方法内部使用

\n
1
2
3
4
5
6
7
8
9
10
package superKeyWord;

//父类:
public class Hero{
public Hero(String name){ \t\t\t//带参的构造方法

System.out.println("调用父类的构造方法"+name);

}
}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package superKeyWord;

public class SuperTest extends Hero {

// super("h2"); //super只能在方法内部使用

public SuperTest(){

super("h2"); //正确,super在方法内使用

System.out.println("调用子类构造方法");
}

public static void main(String[] args){
new Hero("h1");
System.out.println("---------------");
new SuperTest();
}

}

/*输出:
调用父类的构造方法h1
---------------
调用父类的构造方法h2
调用子类构造方法
*/
\n

super调用父类属性

1
2
3
4
5
6
7
8
//父类
package superKeyWord;

public class Hero{

int moveSpeed = 100;

}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//子类

package superKeyWord;

public class SuperTest extends Hero {

int moveSpeed = 200;

public void getMoveSpeed1(){
System.out.println(super.moveSpeed); //打印父类moveSpeed
}

public void getMoveSpeed2() {
System.out.println(this.moveSpeed); //打印子类moveSpeed
}

public static void main(String[] args){

SuperTest h = new SuperTest();
h.getMoveSpeed1(); //输出:100
h.getMoveSpeed2();\t\t\t\t//输出:200
}

}
\n

super调用父类方法

当子类重写了父类的方法后,super调用的依然是父类原本的方法

\n

Object类

Object类是所有类的父类,即声明一个类的时候,默认就继承了Object

\n

Object提供的一些方法

    \n
  • toString():

    \n

    返回当前对象的字符串表达
    通过 System.out.println()打印对象就是打印该对象的toString()返回值

    \n
    1
    2
    3
    4
    5
    6
    Hero h = new Hero();

    //下面两行等效
    System.out.println(h);
    System.out.println(h.toString());

    \n
  • \n
  • finalize():

    \n

    当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件

    \n

    当它被垃圾回收的时候,它的finalize() 方法就会被调用。

    \n

    finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用

    \n
    1
    2
    3
    4
    Hero h;

    h = new Hero();
    h = new Hero();
    \n

    执行第四行的时候,第三行的 Hero() 对象没了引用指向,就满足垃圾回收条件

    \n
  • \n
  • equals():

    \n

    equals() 用于判断两个对象的内容是否相同,假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package charactor;

    public class Hero {
    public String name;
    protected float hp;

    public boolean equals(Object o){
    if(o instanceof Hero){
    Hero h = (Hero) o;
    return this.hp == h.hp;
    }
    return false;
    }

    public static void main(String[] args) {
    Hero h1= new Hero();
    h1.hp = 300;
    Hero h2= new Hero();
    h2.hp = 400;
    Hero h3= new Hero();
    h3.hp = 300;

    System.out.println(h1.equals(h2));
    System.out.println(h1.equals(h3));
    }
    }
    \n
  • \n
\n
    \n
  • ==

    \n

    这不是Object的方法,但是用于判断两个对象是否相同,
    更准确的讲,用于判断两个引用,是否指向了同一个对象

    \n
  • \n
  • hashCode():

    \n

    返回对象的哈希值

    \n
  • \n
  • 线程同步方法

    \n
    1
    2
    3
    wait()
    notify()
    notifyAll()
    \n
  • \n
  • getClass:

    \n

    会返回一个对象的类对象

    \n
  • \n
\n

final修饰

final修饰类,方法,基本类型变量,引用的时候分别有不同的意思。

\n
    \n
  • 修饰类:表示该类不能被继承

    \n
    1
    2
    3
    public final class Hero{   //该类不能被继承

    }
    \n
  • \n
\n
    \n
  • 修饰方法:表示该方法不能被重写

    \n
    1
    2
    3
    public final void useItem(){

    }
    \n
  • \n
\n
    \n
  • 修饰基本变量:表示该变量只能被赋值一次

    \n
    1
    2
    3
    final int a;
    a = 1;
    a = 2; //报错
    \n
  • \n
\n
    \n
  • 修饰引用:表示该引用只有一次指向对象的机会

    \n
    1
    2
    3
    final Hero h;
    h = new Hero();
    h = new Hero(); //报错
    \n
  • \n
\n
\n

可以用 final 修饰变量使其作为常量使用

\n

常量指的是可以公开,直接访问,不会变化的值

\n
\n

抽象类

抽象类

在类中声明一个方法,这个方法没有实现体,是一个“空”方法

\n

这样的方法就叫抽象方法,使用修饰符abstract

\n
\n

注意:

\n
    \n
  • 当一个类有抽象方法的时候,该类必须被声明为抽象类

    \n
  • \n
  • 子类继承抽象类后,必须重写抽象方法(如果有的话),赋予其具体功能

    \n
  • \n
  • 抽象类可以没有抽象方法,可以有实体方法,但是有抽象方法时必须声明为抽象类
  • \n
  • 抽象类不能直接进行实例化,即不能创建抽象类的对象
  • \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
package charactor;

public abstract class Hero { //抽象类

String name;
float hp;
float armor;
int moveSpeed;

public abstract void attack();// 抽象方法attack

}
\n

抽象类与接口区别

    \n
  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  2. \n
  3. 接口中除了 staticfinal 变量,不能有其他变量,而抽象类中则不一定。
  4. \n
  5. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
  6. \n
  7. 接口方法默认修饰符是 public,抽象方法可以有 publicprotecteddefault 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!)。
  8. \n
  9. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
  10. \n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
抽象类接口
抽象类也是类,子类只能继承一个类子类可以实现多个接口
可以是public,protected,package,private只能是public
静态、非静态静态
final或非final的属性final的
\n
\n
\n

另外,抽象类和接口都可以有实体方法。 接口中的实体方法,叫做默认方法

\n
\n

内部类

内部类分为四种:

\n
    \n
  • 非静态内部类

    \n

    非静态内部类,只有一个外部类对象存在的时候,才有意义。也就是说,要调用非静态内部类的属性和方法,必须要先创建一个外部类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Hero{
    String name;

    class BattleScore{ //内部类
    int score;
    public void kill(){

    }
    }
    }
    \n

    调用内部类的途径:

    \n
    1
    2
    Hero h = new Hero();  //先创建外部类
    BattleScore bs = h.new BattleScore();
    \n
  • \n
\n
    \n
  • 静态内部类

    \n

    在一个类里面声明一个静态内部类,静态内部类的实例化不需要一个外部类的实例为基础,可以直接实例化。

    \n

    另外,静态内部类不能直接访问外部类的对象属性

    \n

    语法:

    \n
    1
    new 外部类.静态内部类();
    \n

    例:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Hero{
    String name;

    static class BattleScore{ //静态内部类
    int score;
    public void kill(){

    //静态内部类不能直接访问外部类的对象属性
    System.out.println(name + "kille a Hero");//报错
    }
    }
    }
    \n

    实例化:

    \n
    1
    Hero.BattleScore bs = new Hero.BattleScore();
    \n
  • \n
\n
    \n
  • 匿名类

    \n

    匿名类指的是在创建某个类的对象的同时实例化它,使代码更加简洁精练
    通常情况下,要使用一个接口或者抽象类,都必须创建一个子类,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
    既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做匿名类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package charactor;

    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack(); //抽象方法

    public static void main(String[] args) {

    Hero h = new Hero(){
    //当场实现attack方法
    public void attack() {
    System.out.println("新的进攻手段");
    }
    };
    h.attack();
    //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名

    System.out.println(h);
    }

    }
    \n

    注意:在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final,但在jdk8中,已经不需要强制修饰成final了,因为编译器偷偷的帮你加上了看不见的final

    \n
  • \n
  • 本地类

    \n

    本地类可以理解为有名字的匿名类

    \n
      \n
    • 内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。
    • \n
    • 本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package charactor;

    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack();

    public static void main(String[] args) {

    //在主方法里声明本地类
    //与匿名类的区别在于,本地类有了自定义的类名
    class SomeHero extends Hero{
    public void attack() {
    System.out.println( name+ " 新的进攻手段");
    }
    }

    SomeHero h =new SomeHero();
    h.name ="地卜师";
    h.attack();
    }

    }
    \n
  • \n
\n

默认方法

默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法

\n

例如:

\n
1
2
3
4
5
6
7
8
public interface Mortal {
public void die(); //抽象方法

default public void revive() { //具体方法

System.out.println("本英雄复活了");
}
}
\n
\n

为什么会有默认方法?

\n

假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的子类,都需要做改动(都要在子类中考虑重写revive的具体方法)。

\n

但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法

\n

通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类

\n
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

接口与继承

接口

接口

接口的创建:(与创建类相似)

\n
1
2
3
4
5
6
7
//创建一个播放器接口
public interface Player{
public void play();
public void pause();
public void stop();
public void tune(); //接口只有方法声明,没有主体,并且默认都是public的,可以不写“public”
}
\n

Java接口是一系列方法的声明(没有主体),是一些方法特征的集合;

\n

一个接口只有方法的特征没有方法的实现(不能使用 new来创建实例),因此这些方法可以在不同的地方被不同的类实现(继承),进而在不同的类中实现不同的功能。

\n

接口就是一组抽象方法和常量值的集合。可以把接口看成是一种特殊的抽象类。

\n

(1)其所有的方法都必须是抽象的(abstract)。

\n

(2)其属性成员(若有)只能是final static的常量。

\n

实现接口

实现一个接口与类的继承相似,用 implements 来实现

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建一个MP3类,实现播放器功能
public MP3 implements Player{
public void play(){
System.out.println("播放");
}

public void pause(){
System.out.println("暂停");
}

public void stop(){
System.out.println("停止");
}

public void tune(){
System.out.println("调节音量");
}
}
\n

对象转型

明确引用类型与对象类型的概念

引用和对象都是有类型的

\n
1
Hero h = new Hero();
\n

在这个例子,引用是 h,对象是 new Hero(),它们的类型均为 Hero

\n

通常情况引用类型与对象类型是一样的,但也有不一样的时候

\n

子类转父类(向上转型)

所谓的转型,是指当引用类型对象类型不一致的时候,才需要进行类型转换;
类型转换有时候会成功,有时候会失败

\n

假如 ADHeroHero 的子类

\n
1
2
3
Hero h = new Hero();
ADHero ad = new ADHero();

\n

用图来表示其关系如下:

\n

\"image-20210126224549229\"

\n
1
h = ad;                      //向上转型
\n

转型后的图:

\n

\"image-20210126224655825\"

\n

可以看到 h 经过 ad 最终指向的还是 Hero

\n

因此,把 h 指向 ADHero 一定可以,因为 ADHero 继承了 Hero,Hero能实现的功能 ADHero 也可以

\n

父类转子类(向下转型)

父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。
强制转换的意思就是:转换有风险,风险自担。

\n

假如 ADHeroSupportHero 的两个不同子类:

\n
1
2
3
Hero h =new Hero();
ADHero ad = new ADHero();
Support s = new Support();
\n
1
h = s;            // 向上转型,一定可以
\n

向上转型一定可以,因为 h通过 s 最终还是指向 Hero

\n

\"image-20210126225703371\"

\n
1
ad = (ADHero)h;    //向下转型,不一定可以,需要强制转换
\n

虽然 ad 指向的 ADHeroHero 的子类,眨眼看来 ad 最终也是指向 Hero 的,但实际上指向的是 ADHero,ADHero与Hero还是有区别的,ADHero有的方法Hero不一定有

\n

而ad通过h最终指向的是 Hero,而不是 ADHero,因此会有风险(指向ADHero才不会有风险),只能强制转换。

\n

\"image-20210126230408061\"

\n

因为子类拥有的方法父类不一定有,因此 h 向下转型后,ad可能就没有了一些子类的方法

\n

没有继承关系的两个类

没有继承关系的两个类,互相转换,一定会失败

\n

实现类转换成接口(向上转型)

假如 AD 是一个接口,ADHero 类继承了它

\n
1
2
3
4
5
6
7
public class Test {
public static void main(String[] args) {
ADHero ad = new ADHero();

AD adi = ad; //向上转型
}
}
\n

转型练习

如下转换能否成功?如果不能,是哪一行会出错?为什么会出错?

\n
1
2
3
4
5
6
7
8
9
10
11
public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {
ADHero ad = new ADHero();
Hero h = ad;
AD adi = (AD) h;
APHero ap = (APHero) adi;
}
}
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
分析
第7行向上转型没问题
第8行,可以将h强制转换为AD,因为 AD 与 Hero 之间通过子类关联在了一起;因此 h 指向了AD,而通过 h -> ad -> ADHero -> AD,最终也是指向AD,虽有风险,但不会报错;
第9行,不能将 adi 强制转换为 APHero,因为 AD 与 APHero之间没有关联,因此ap本应该指向 APHero,但通过转换 ap -> adi -> AD,即最终指向了AD,因此不能进行转换,会报错
\n
\n

\"image-20210126234924701\"

\n

instanceof 语句

instanceof Hero 判断一个引用所指向的对象,是否是以下两种,返回 true 或 false

\n
    \n
  • Hero类型
  • \n
  • Hero的子类
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package charactor;

public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {
ADHero ad = new ADHero();
APHero ap = new APHero();

Hero h1= ad;
Hero h2= ap;

//判断引用h1指向的对象,是否是ADHero类型
System.out.println(h1 instanceof ADHero); \t//true

//判断引用h2指向的对象,是否是APHero类型
System.out.println(h2 instanceof APHero);\t//true

//判断引用h1指向的对象,是否是Hero的子类型
System.out.println(h1 instanceof Hero);\t\t//true
}
}
\n

重写

子类可以继承父类的对象方法

\n

在继承后,重复提供该方法,就叫做方法的重写

\n

又叫覆盖 override

\n
\n

父类Item有一个方法,叫做effect

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package property;

public class Item {
String name;
int price;

public void buy(){
System.out.println("购买");
}
public void effect() {
System.out.println("物品使用后,可以有效果");
}

}
\n

子类LifePotion继承Item,同时也提供了方法effect

\n
1
2
3
4
5
6
7
8
9
package property;

public class LifePotion extends Item{

public void effect(){ \t\t\t\t\t\t//重写effect()方法
System.out.println("血瓶使用后,可以回血");
}

}
\n

调用重写的方法
调用就会执行重写的方法,而不是从父类的方法

\n
\n

重写的优点:

\n

如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。

\n

但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能放弃继承Item,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动.

\n

这样就增加了开发时间和维护成本

\n

多态

操作符的多态

同一个操作符在不同情境下,具备不同的作用:

\n
    \n
  • 如果+号两侧都是整型,那么+代表 数字相加
  • \n
  • 如果+号两侧,任意一个是字符串,那么+代表字符串连接
  • \n
\n

类的多态

多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态(实际是子类重写了父类的方法,导致子类的方法与父类的方法不同,子类之间的方法也不同)

\n

类的多态即父类引用指向不同的子类对象,调用同一个方法时出现不同的效果

\n
\n

假设 Hero是 ADHeroAPHero 的父类

\n
1
2
Hero h1 = new ADHero();
Hero h2 = new APHero();
\n
\n

类的多态的条件:

\n
    \n
  1. 父类(接口)引用指向子类对象

    \n
  2. \n
  3. 调用的方法有重写

    \n
  4. \n
\n

使用类多态的好处

如果不使用多态,例如:

\n
\n

假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法
useLifePotion、useMagicPotion;
除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如
usePurityPotion、useGuard、useInvisiblePotion等等等等

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package charactor;

import property.LifePotion;
import property.MagicPotion;

public class Hero {
public String name;
protected float hp;

public void useLifePotion(LifePotion lp){
lp.effect();
}
public void useMagicPotion(MagicPotion mp){
mp.effect();
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";

LifePotion lp =new LifePotion();
MagicPotion mp =new MagicPotion();

garen.useLifePotion(lp);
garen.useMagicPotion(mp);

}

}
\n

这个时候采用多态来解决这个问题:

\n
\n

设计一个方法叫做useItem,其参数类型是Item

\n
    \n
  • 如果是使用血瓶,调用该方法
  • \n
  • 如果是使用魔瓶,还是调用该方法
  • \n
  • 无论英雄要使用什么样的物品,只需要一个方法 即可
  • \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package charactor;

import property.Item;
import property.LifePotion;
import property.MagicPotion;

public class Hero {
public String name;
protected float hp;

public void useItem(Item i){ //设计一个方法叫做useItem,其参数类型是Item
i.effect();
}

public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";

LifePotion lp =new LifePotion();
MagicPotion mp =new MagicPotion();

garen.useItem(lp); //使用血瓶lp
garen.useItem(mp); //使用魔瓶mp

}

}
\n

隐藏

与重写类似:

\n
    \n
  • 重写,是子类覆盖父类的对象方法
  • \n
  • 隐藏,就是子类覆盖父类的类方法
  • \n
\n

如何隐藏呢:

\n
\n

父类有一个类方法 :battleWin

\n
1
2
3
4
5
6
7
8
9
10
11
package charactor;

public class Hero {
public String name;
protected float hp;

public static void battleWin(){ //类方法,静态方法
System.out.println("hero battle win");
}

}
\n

创建一个子类,隐藏父类方法 battleWin()

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package charactor;

public class ADHero extends Hero implements AD{

//隐藏父类的battleWin方法
public static void battleWin(){
System.out.println("ad hero battle win");
}

public static void main(String[] args) {
Hero.battleWin(); // 调用父类的方法
ADHero.battleWin();//调用子类的方法
}

}
\n

问:对于Hero h =new ADHero();

\n

h是父类类型的引用,但是指向一个子类对象h.battleWin(); 会调用父类的方法?还是子类的方法?

\n

答:当父类的引用指向一个子类对象时,执行的:

\n
    \n
  • 对象方法是子类的对象方法(因为重写)
  • \n
  • 类方法是父类的类方法(类方法不能被重写)
  • \n
\n
\n

super关键字

super()用于子类的构造函数内部;

\n

特点:

\n
    \n
  1. 实例化一个父类的时候,父类的构造方法会被自动调用(根据实例化方式来选择调用有参或无参的构造方法);

    \n
  2. \n
  3. 实例化一个子类的时候,若没有写 super()语句,会默认调用父类无参构造方法。

    \n
  4. \n
  5. 并且,父类和子类的构造方法都会被调用,且父类的构造方法被调用

    \n
  6. \n
\n
1
2
3
4
5
6
7
package superKeyWord;
//父类:
public class Hero{
public Hero(){
System.out.println("调用父类的构造方法");
}
}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package superKeyWord;

//子类
public class SuperTest extends Hero {
public SuperTest(){
System.out.println("调用子类构造方法");
}

public static void main(String[] args){
new Hero();\t\t\t\t\t\t\t//创建父类对象
System.out.println("---------------");
new SuperTest();\t\t\t\t\t//创建子类对象
}

}

/*输出:
调用父类的构造方法
---------------
调用父类的构造方法
调用子类构造方法
*/
\n

super调用父类带参构造方法

super() 相当于一个父类的对象,在子类中使用就类似于创建了一个父类的对象,会调用父类带参的构造方法

\n

superthis 类似,this是当前类的对象,super是父类的对象,两者都只能在方法内部使用

\n
1
2
3
4
5
6
7
8
9
10
package superKeyWord;

//父类:
public class Hero{
public Hero(String name){ \t\t\t//带参的构造方法

System.out.println("调用父类的构造方法"+name);

}
}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package superKeyWord;

public class SuperTest extends Hero {

// super("h2"); //super只能在方法内部使用

public SuperTest(){

super("h2"); //正确,super在方法内使用

System.out.println("调用子类构造方法");
}

public static void main(String[] args){
new Hero("h1");
System.out.println("---------------");
new SuperTest();
}

}

/*输出:
调用父类的构造方法h1
---------------
调用父类的构造方法h2
调用子类构造方法
*/
\n

super调用父类属性

1
2
3
4
5
6
7
8
//父类
package superKeyWord;

public class Hero{

int moveSpeed = 100;

}
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//子类

package superKeyWord;

public class SuperTest extends Hero {

int moveSpeed = 200;

public void getMoveSpeed1(){
System.out.println(super.moveSpeed); //打印父类moveSpeed
}

public void getMoveSpeed2() {
System.out.println(this.moveSpeed); //打印子类moveSpeed
}

public static void main(String[] args){

SuperTest h = new SuperTest();
h.getMoveSpeed1(); //输出:100
h.getMoveSpeed2();\t\t\t\t//输出:200
}

}
\n

super调用父类方法

当子类重写了父类的方法后,super调用的依然是父类原本的方法

\n

Object类

Object类是所有类的父类,即声明一个类的时候,默认就继承了Object

\n

Object提供的一些方法

    \n
  • toString():

    \n

    返回当前对象的字符串表达
    通过 System.out.println()打印对象就是打印该对象的toString()返回值

    \n
    1
    2
    3
    4
    5
    6
    Hero h = new Hero();

    //下面两行等效
    System.out.println(h);
    System.out.println(h.toString());

    \n
  • \n
  • finalize():

    \n

    当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件

    \n

    当它被垃圾回收的时候,它的finalize() 方法就会被调用。

    \n

    finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用

    \n
    1
    2
    3
    4
    Hero h;

    h = new Hero();
    h = new Hero();
    \n

    执行第四行的时候,第三行的 Hero() 对象没了引用指向,就满足垃圾回收条件

    \n
  • \n
  • equals():

    \n

    equals() 用于判断两个对象的内容是否相同,假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package charactor;

    public class Hero {
    public String name;
    protected float hp;

    public boolean equals(Object o){
    if(o instanceof Hero){
    Hero h = (Hero) o;
    return this.hp == h.hp;
    }
    return false;
    }

    public static void main(String[] args) {
    Hero h1= new Hero();
    h1.hp = 300;
    Hero h2= new Hero();
    h2.hp = 400;
    Hero h3= new Hero();
    h3.hp = 300;

    System.out.println(h1.equals(h2));
    System.out.println(h1.equals(h3));
    }
    }
    \n
  • \n
\n
    \n
  • ==

    \n

    这不是Object的方法,但是用于判断两个对象是否相同,
    更准确的讲,用于判断两个引用,是否指向了同一个对象

    \n
  • \n
  • hashCode():

    \n

    返回对象的哈希值

    \n
  • \n
  • 线程同步方法

    \n
    1
    2
    3
    wait()
    notify()
    notifyAll()
    \n
  • \n
  • getClass:

    \n

    会返回一个对象的类对象

    \n
  • \n
\n

final修饰

final修饰类,方法,基本类型变量,引用的时候分别有不同的意思。

\n
    \n
  • 修饰类:表示该类不能被继承

    \n
    1
    2
    3
    public final class Hero{   //该类不能被继承

    }
    \n
  • \n
\n
    \n
  • 修饰方法:表示该方法不能被重写

    \n
    1
    2
    3
    public final void useItem(){

    }
    \n
  • \n
\n
    \n
  • 修饰基本变量:表示该变量只能被赋值一次

    \n
    1
    2
    3
    final int a;
    a = 1;
    a = 2; //报错
    \n
  • \n
\n
    \n
  • 修饰引用:表示该引用只有一次指向对象的机会

    \n
    1
    2
    3
    final Hero h;
    h = new Hero();
    h = new Hero(); //报错
    \n
  • \n
\n
\n

可以用 final 修饰变量使其作为常量使用

\n

常量指的是可以公开,直接访问,不会变化的值

\n
\n

抽象类

抽象类

在类中声明一个方法,这个方法没有实现体,是一个“空”方法

\n

这样的方法就叫抽象方法,使用修饰符abstract

\n
\n

注意:

\n
    \n
  • 当一个类有抽象方法的时候,该类必须被声明为抽象类

    \n
  • \n
  • 子类继承抽象类后,必须重写抽象方法(如果有的话),赋予其具体功能

    \n
  • \n
  • 抽象类可以没有抽象方法,可以有实体方法,但是有抽象方法时必须声明为抽象类
  • \n
  • 抽象类不能直接进行实例化,即不能创建抽象类的对象
  • \n
\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
package charactor;

public abstract class Hero { //抽象类

String name;
float hp;
float armor;
int moveSpeed;

public abstract void attack();// 抽象方法attack

}
\n

抽象类与接口区别

    \n
  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  2. \n
  3. 接口中除了 staticfinal 变量,不能有其他变量,而抽象类中则不一定。
  4. \n
  5. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
  6. \n
  7. 接口方法默认修饰符是 public,抽象方法可以有 publicprotecteddefault 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!)。
  8. \n
  9. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
  10. \n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
抽象类接口
抽象类也是类,子类只能继承一个类子类可以实现多个接口
可以是public,protected,package,private只能是public
静态、非静态静态
final或非final的属性final的
\n
\n
\n

另外,抽象类和接口都可以有实体方法。 接口中的实体方法,叫做默认方法

\n
\n

内部类

内部类分为四种:

\n
    \n
  • 非静态内部类

    \n

    非静态内部类,只有一个外部类对象存在的时候,才有意义。也就是说,要调用非静态内部类的属性和方法,必须要先创建一个外部类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Hero{
    String name;

    class BattleScore{ //内部类
    int score;
    public void kill(){

    }
    }
    }
    \n

    调用内部类的途径:

    \n
    1
    2
    Hero h = new Hero();  //先创建外部类
    BattleScore bs = h.new BattleScore();
    \n
  • \n
\n
    \n
  • 静态内部类

    \n

    在一个类里面声明一个静态内部类,静态内部类的实例化不需要一个外部类的实例为基础,可以直接实例化。

    \n

    另外,静态内部类不能直接访问外部类的对象属性

    \n

    语法:

    \n
    1
    new 外部类.静态内部类();
    \n

    例:

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Hero{
    String name;

    static class BattleScore{ //静态内部类
    int score;
    public void kill(){

    //静态内部类不能直接访问外部类的对象属性
    System.out.println(name + "kille a Hero");//报错
    }
    }
    }
    \n

    实例化:

    \n
    1
    Hero.BattleScore bs = new Hero.BattleScore();
    \n
  • \n
\n
    \n
  • 匿名类

    \n

    匿名类指的是在创建某个类的对象的同时实例化它,使代码更加简洁精练
    通常情况下,要使用一个接口或者抽象类,都必须创建一个子类,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
    既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做匿名类

    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package charactor;

    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack(); //抽象方法

    public static void main(String[] args) {

    Hero h = new Hero(){
    //当场实现attack方法
    public void attack() {
    System.out.println("新的进攻手段");
    }
    };
    h.attack();
    //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名

    System.out.println(h);
    }

    }
    \n

    注意:在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final,但在jdk8中,已经不需要强制修饰成final了,因为编译器偷偷的帮你加上了看不见的final

    \n
  • \n
  • 本地类

    \n

    本地类可以理解为有名字的匿名类

    \n
      \n
    • 内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。
    • \n
    • 本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方
    • \n
    \n
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package charactor;

    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack();

    public static void main(String[] args) {

    //在主方法里声明本地类
    //与匿名类的区别在于,本地类有了自定义的类名
    class SomeHero extends Hero{
    public void attack() {
    System.out.println( name+ " 新的进攻手段");
    }
    }

    SomeHero h =new SomeHero();
    h.name ="地卜师";
    h.attack();
    }

    }
    \n
  • \n
\n

默认方法

默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法

\n

例如:

\n
1
2
3
4
5
6
7
8
public interface Mortal {
public void die(); //抽象方法

default public void revive() { //具体方法

System.out.println("本英雄复活了");
}
}
\n
\n

为什么会有默认方法?

\n

假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的子类,都需要做改动(都要在子类中考虑重写revive的具体方法)。

\n

但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法

\n

通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类

\n
\n"},{"title":"16-文件IO","abbrlink":"4f0e7b50","date":"2021-02-16T02:11:35.000Z","description":"java中关于文件IO的操作","cover":"https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png","_content":"\n\n\n\n# 文件IO系列\n\n\n\n文件和文件夹都是用`File`代表\n\n\n\n## 文件对象\n\n### 创建一个文件对象\n\n> 注意:不是创建文件\n\n首先导入 `File`类\n\n```java\nimport java.io.File;\n```\n\n使用绝对路径或者相对路径创建File对象\n\n```java\n// 绝对路径\nFile f1 = new File(\"d:/LOLFolder\"); //这是一个文件夹\n\n// 相对路径, 相对于工作目录(项目目录)\nFile f2 = new File(\"LOL.exe\");\t\t//这是一个文件\n```\n\n```java\n// 把f1作为父目录创建文件对象\nFile f3 = new File(f1, \"LOL.exe\");\n```\n\n### 文件常用方法\n\n```java\nFile f = new File(\"d:/LOLFolder/skin/garen.ski\");\n```\n\n\n\n- 获取文件相关信息:\n\n```java\nf.exists();\t\t//判断文件是否存在\n\nf.isDirectory();\t//判断是否是文件夹\n\nf.isFile(); //判断是否是文件\n\nf.length(); //文件长度(单位字节bytes)\n\nlong time = f.lastModified(); //返回从1970-1-1 08:00:00 开始的秒数\nDate d = new Date(time);\n\n\nf.setLastModified(0);\t//设置文件修改时间为1970.1.1 08:00:00\n\n```\n\n\n\n- 文件、文件夹操作\n\n```java\nf.list();\n// 以【字符串数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)\n\nFile[]fs = f.listFiles();\n// 以【文件(后缀为Files)数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)\n\nf.getParent();\n// 以【字符串】形式返回文件所在文件夹\n\nf.getParentFile();\n// 以【文件】形式返回获取所在文件夹\n\nf.mkdir();\n// 创建文件夹,如果父文件夹skin不存在,创建就无效\n\nf.mkdirs();\n// 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹\n\nf.createNewFile();\n// 创建一个空文件,如果父文件夹skin不存在,就会抛出异常\n\nf.getParentFile().mkdirs();\n// 所以创建一个空文件之前,通常都会创建父目录\n\nf.listRoots();\n// 列出所有的盘符c: d: e: 等等\n\nf.delete();\n// 刪除文件\n\nf.deleteOnExit();\n// JVM结束的时候,刪除文件,常用于临时文件的删除\n```\n\n\n\n## 什么是流\n\n> 流(Stream)就是一系列的数据\n\n当不同的介质之间有数据交互的时候,JAVA就使用流来实现。\n数据源可以是**文件**,**数据库**,**网络**,甚至是**其他的程序**。\n\n比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流\n\n(输入输出是针对Java虚拟机JVM而言的,流入JVM叫输入,反之叫输出)\n\n- 输入流:InputStream\n- 输出流:OutputStream\n\n![什么是流](https://stepimagewm.how2j.cn/759.png)\n\n### 文件输入流\n\njava中通过 `FileInputStream()` 实现文件输入流。\n\n如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。\n\n目前代码只是建立了流,还没有开始读取,真正的读取在下个章节讲解。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n try {\n File f = new File(\"d:/lol.txt\");//创建文件对象\n \n // 创建基于文件的输入流,即将文件对象放入流中\n FileInputStream fis = new FileInputStream(f);\n // 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中\n \n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 文件输出流\n\n`FileOutputStream` :通过这个输出流,就可以吧数据从java的虚拟机中写入硬盘\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\n \npublic class TestStream {\n    public static void main(String[] args) {\n        File f =new File(\"d:/lol.txt\");\n        try {\n            FileOutputStream fos = new FileOutputStream(f);\n        } catch (FileNotFoundException e) {\n            e.printStackTrace();\n        }\n \n    }\n}\n```\n\n\n\n## 字节流\n\n字节流即:用于以字节的形式读取和写入数据\n\nInputStream:字节输入流\nOutputStream:字节输出流\n\n### ASCII码概念\n\n所有的数据存放在计算机中都是以数字的形式存放的。 所以**字母就需要转换为数字才能够存放**。\n\n比如A就对应的数字65,a对应的数字97. 不同的**字母和符号**对应不同的数字,就是一张码表。\n\n> ASCII是这样的一种码表。 只包含简单的英文字母、符号、数字等。 **不包含中文,德文,俄语等复杂**的。\n\n### 以字节流形式读取文件内容\n\n`InputStream`是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。\n\n`FileInputStream` 是InputStream 子类,以 FileInputStream 为例进行文件读取\n\n```java\npackage IOlearning.stream;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n\npublic class StreamTest {\n\n public static void main(String[] args) {\n try {\n \n File f = new File(\"d:/javaTest.txt\"); //文件javaTest.txt内容是abc...xyz\n\n // 创建基于文件的输入流\n FileInputStream fis = new FileInputStream(f);\n\n // 创建字节数组,其长度就是文件的长度\n byte[] all = new byte[(int) f.length()];\n \n fis.read(all); // 以字节流的形式读取文件所有内容到 all\n \n for (byte b : all) {\n System.out.print(b + \" \");\n// 打印出来是97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122\n }\n\n // 每次使用完流,都应该进行关闭\n fis.close();\n\n } catch (IOException e) {\n e.printStackTrace();\n }\n\n }\n}\n```\n\n### 以字节流的形式向文件写入数据\n\n`OutputStream` 是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。\n\n`FileOutputStream` 是OutputStream子类,以FileOutputStream 为例向文件写出数据\n\n> **注**: 若文件d:/lol2.txt不存在,写出操作会***自动创建***该文件。\n> 但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n try {\n // 准备文件javaTest2.txt, 其中的内容是空的\n File f = new File(\"d:/javaTest2.txt\");\n \n // 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y\n byte data[] = { 88, 89 };\n \n // 创建基于文件的输出流\n FileOutputStream fos = new FileOutputStream(f);\n \n fos.write(data);// 把数据写入到输出流,注意data是个数组\n \n // 关闭输出流\n fos.close();\n \n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 总结(利用流读取文件步骤)\n\n4个操作步骤:\n\n- 创建文件对象\n\n- 将文件放入对应的流(输入、输出流)\n- 操作流(读取、写入)\n- 关闭流(close)\n\n\n\n## 关闭流的方式\n\n### 在try里关闭\n\n```java\ntry {\n\n File f = new File(\"d:/javaTest.txt\"); \n FileInputStream fis = new FileInputStream(f);\n byte[] all = new byte[(int) f.length()];\n fis.read(all); \n\n //使用完流,进行关闭\n fis.close();\n\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个**弊端**:\n如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 **不推荐**使用\n\n### 在finally里关闭\n\n这是**标准的**关闭流的方式\n\n1. 首先把流的**引用声明**在try的外面,如果声明在try里面,其作用域无法抵达finally.\n\n2. 在finally关闭之前,要先判断该引用是否为空\n\n3. 关闭的时候,需要再一次进行try catch处理\n\n> 这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的[有隐患](#在try里关闭)的方式,因为不麻烦🤣\n\n下面是标准方式:\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:/lol.txt\");\n FileInputStream fis = null;\t\t//在try外部声明\n \n try {\n fis = new FileInputStream(f);\n byte[] all = new byte[(int) f.length()];\n fis.read(all);\n for (byte b : all) {\n System.out.println(b);\n }\n \n } catch (IOException e) {\n e.printStackTrace();\n } finally {\n \n // 在finally 里关闭流\n if (null != fis)\n try {\n \n fis.close();\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n }\n \n }\n}\n```\n\n### 使用try()的方式\n\n把流定义在try()里,当try、catch或者finally结束的时候,会自动关闭。注意区别[在try里关闭](#在try里关闭)\n\n这种编写代码的方式叫做 **try-with-resources**, 这是从JDK7开始支持的技术。\n\n> 所有的流,都实现了一个**接口**叫做 `AutoCloseable`,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:/lol.txt\");\n \n //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭\n try (FileInputStream fis = new FileInputStream(f)) {\n \n byte[] all = new byte[(int) f.length()];\n fis.read(all);\n for (byte b : all) {\n System.out.println(b);\n }\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n## 字符流\n\n上面所讲的 `InputStream` 与 `OutputStream` 是字节流;\n\n这里还有字符流,即专门用于以字符的形式读写数据:\n\n- Reader\n- Writer\n\n### 用字符流读取文件\n\n`FileReader` 是 Reader子类,以FileReader 为例进行文件读取\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n \n // 准备文件lol.txt其中的内容是AB\n File f = new File(\"d:/lol.txt\");\n \n // 创建基于文件的Reader\n try (FileReader fr = new FileReader(f)) {\n \n // 创建字符数组,其长度就是文件的长度\n char[] all = new char[(int) f.length()];\n \n // 以字符流的形式读取文件所有内容\n fr.read(all);\n \n for (char b : all) {\n // 打印出来是A B\n System.out.println(b);\n }\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### 用字符流把字符串写入文件\n\n`FileWriter` 是Writer的子类,以FileWriter 为例把字符串写入到文件\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n // 准备文件lol2.txt\n File f = new File(\"d:/lol2.txt\");\n // 创建基于文件的Writer\n try (FileWriter fr = new FileWriter(f)) {\n \n // 以字符流的形式把数据写入到文件中\n String data=\"abcdefg1234567890\";\n char[] cs = data.toCharArray();\n fr.write(cs);\n \n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n## 中文问题\n\n### 编码概念\n\nASCII 字符集只有256个字符,用 0-255 之间的数字来表示。包括大小写字母、数字以及少数特殊字符:如标点符号、货币符号等。\n\n对于大多数拉丁语言来说,这些字符已经够用。\n\n但是,许多亚洲和东方语言所用的字符远远不止256个字符。有些超过千个。\n\n因此,为了突破 ASCII 码字符数的限制,试图用新的编码方法来针对超过256个字符的语言编写计算机程序\n\n### 常见编码\n\n工作后经常接触的编码方式有如下几种:\n\n- ISO-8859-1/ASCII: 数字和西欧字母\n- GBK/GB2312/BIG5: 中文\n- UNICODE: 统一码,万国码\n\n其中\n\n- ISO-8859-1 包含 ASCII\n- GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。\n- UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中\n\n### UNICODE和UTF\n\n虽然UNICODE可以存储所有字符,但如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。因为1个Unicode字符就占用`4 bytes`\n\n倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间\n\n在这种情况下,就出现了UNICODE的各种**减肥**子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了**减肥还能保证健康**的效果\n\nUTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的**减肥效果**,一般说来UTF-8是比较常用的方式\n\nUTF-8,UTF-16和UTF-32 彼此的区别在此不作赘述,有兴趣的可以参考 [unicode-百度百科](http://baike.baidu.com/link?url=ty4mEX5hSfK2xAyPO8N2zgxTibBE59CShSb5yFxbVkBun_QVz65llOPEXOepgPeqe3AQDLt6LLjTayn6tioS4_#4)\n\n> UTF-8编码方式:数字和字母用一个字节, 汉字用3个字节。\n\n### Java采用的是Unicode\n\n写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。\n而这些中文字符采用的编码方式,都是使用UNICODE.\n\n例如: \"中\"字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。\n\n### 一个汉字使用不同编码方式的表现\n\n以字符 **中** 为例,查看其在不同编码方式下的值是多少\n\n```java\npackage stream;\n \nimport java.io.UnsupportedEncodingException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n String str = \"中\";\n showCode(str);\n }\n \n private static void showCode(String str) {\n String[] encodes = { \"BIG5\", \"GBK\", \"GB2312\", \"UTF-8\", \"UTF-16\", \"UTF-32\" };\n for (String encode : encodes) {\n showCode(str, encode);\n }\n \n }\n \n private static void showCode(String str, String encode) {\n try {\n System.out.printf(\"字符: \\\"%s\\\" 的在编码方式%s下的十六进制值是%n\", str, encode);\n byte[] bs = str.getBytes(encode);\n \n for (byte b : bs) {\n int i = b&0xff;\n System.out.print(Integer.toHexString(i) + \"\\t\");\n }\n System.out.println();\n System.out.println();\n } catch (UnsupportedEncodingException e) {\n System.out.printf(\"UnsupportedEncodingException: %s编码方式无法解析字符%s\\n\", encode, str);\n }\n }\n}\n```\n\n![image-20210210233822081](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210210233822081.png)\n\n### 用FileInputStream 字节流正确读取中文\n\n为了能够正确的读取中文内容\n\n1. 必须了解文本是以哪种编码方式保存字符的\n\n2. 使用字节流读取文本\n3. 使用对应的**编码方式去识别这些数字**,得到正确的字符。 \n\n如本例,一个文件中的内容是字符\"中\",编码方式是GBK,那么读出来的数据一定是D6D0。\n\n再使用GBK编码方式识别D6D0,就能正确的得到字符**中**\n\n> **注:** 在GBK的棋盘上找到的**中**字后,JVM会自动找到**中**在UNICODE这个棋盘上对应的数字,并且以[UNICODE上的数字保存在内存中](https://how2j.cn/k/io/io-encoding/695.html#step2486)。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:\\\\project\\\\j2se\\\\src\\\\test.txt\"); //该文件保存时的编码方式为GBK\n \n try (FileInputStream fis = new FileInputStream(f);) {\n \n byte[] all = new byte[(int) f.length()];\n \n fis.read(all);\t//使用字节流读取文本\n \n String str = new String(all,\"GBK\"); //解码,解码方式为GBK\n System.out.println(str);\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 用FileReader 字符流正确读取中文\n\nFileReader得到的是字符,所以一定是已经把字节**根据某种编码识别成为字符**了\n\n而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK\n\nFileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:\n\n```java\nnew InputStreamReader(new FileInputStream(f), Charset.forName(\"UTF-8\")); \n```\n\n\n\n## 缓存流\n\n以介质是硬盘为例,**字节流和字符流的弊端**:\n在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。\n\n为了解决以上弊端,采用缓存流。\n缓存流在读取的时候,**会一次性读较多的数据到缓存中**,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。\n\n就好比吃饭,**不用缓存就是每吃一口都到锅里去铲**。**用缓存就是先把饭盛到碗里**,碗里的吃完了,再到锅里去铲。\n\n缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区**达到一定的量**,才把这些数据,**一起写入到硬盘中去**。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作,提高速度。\n\n### 使用缓存流读取数据\n\n缓存字符输入流 `BufferedReader` 可以一次读取一行数据,但要注意,缓存流必须建立在一个存在的流的基础上\n\n> 先准备好文件 `d:/lol.txt`,文件内容如下:\n>\n> garen kill teemo\n> teemo revive after 1 minutes\n> teemo try to garen, but killed again\n\n```java\npackage stream;\n \nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n\n File f = new File(\"d:/lol.txt\");\n \n try (\n FileReader fr = new FileReader(f);\t\t// 创建文件字符流\n BufferedReader br = new BufferedReader(fr); // 缓存流必须建立在一个存在的流的基础上\n )\n {\n while (true) {\n String line = br.readLine();// 一次读一行\n if (line == null)\n break;\n System.out.println(line);\n }\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### 使用缓存流写入数据\n\n[之前](#以字节流的形式向文件写入数据)的 `FileOutputStream` 与 `FileWriter`要写入一串数据时,必须将数据转换为数组,一次只能写入一个字符。\n\n而`PrintWriter` 缓存字符输出流, 可以一次写入一行数据;\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.PrintWriter;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n // 向文件lol2.txt中写入三行语句\n File f = new File(\"d:/lol2.txt\");\n \n try (\n FileWriter fw = new FileWriter(f); // 创建文件字符流\n PrintWriter pw = new PrintWriter(fw); // 缓存流必须建立在一个存在的流的基础上 \n \n ) {\n pw.println(\"garen kill teemo\"); //写入一行数据\n pw.println(\"teemo revive after 1 minutes\");\n pw.println(\"teemo try to garen, but killed again\");\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### flush方法\n\n有的时候,需要**立即把数据写入到硬盘**,而不是等缓存满了才写出去。 这时候就需要用到`flush()`方法\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.PrintWriter;\npublic class TestStream {\n public static void main(String[] args) {\n //向文件lol2.txt中写入三行语句\n File f =new File(\"d:/lol2.txt\");\n \n try(\n FileWriter fr = new FileWriter(f); //创建文件字符流\n PrintWriter pw = new PrintWriter(fr); //缓存流必须建立在一个存在的流的基础上\n ) {\n pw.println(\"garen kill teemo\");\n pw.flush(); //强制把缓存中的数据写入硬盘,无论缓存是否已满\n pw.println(\"teemo revive after 1 minutes\");\n pw.flush();\n pw.println(\"teemo try to garen, but killed again\");\n pw.flush();\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n }\n}\n```\n\n\n\n## 数据流\n\n- DataInputStream 数据输入流\n- DataOutputStream 数据输出流\n\n### 直接读写字符串\n\n使用数据流的`writeUTF()`和`readUTF()` 可以进行数据的**格式化顺序读写**;\n如本例,通过DataOutputStream 向文件顺序写出【布尔值,整数和字符串】。 然后再通过DataInputStream 顺序读入这些数据。\n\n> **注:** 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException\n>\n> 因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。\n\n读取步骤:\n\n1. 创建输入流 `FileInputStream()`\n2. 创建数据输入流 `DataInputStream()`\n3. - 读取布尔值:`boolean b= dis.readBoolean();`\n - 读取整数:`int i = dis.readInt();`\n - 读取字符串:`String str = dis.readUTF();`\n\n```java\nFile f =new File(\"d:/lol.txt\");\ntry (\n FileInputStream fis = new FileInputStream(f);\n DataInputStream dis =new DataInputStream(fis);\n){\n boolean b= dis.readBoolean();\n int i = dis.readInt();\n String str = dis.readUTF();\n\n System.out.println(\"读取到布尔值:\"+b);\n System.out.println(\"读取到整数:\"+i);\n System.out.println(\"读取到字符串:\"+str);\n\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n写入步骤:\n\n1. 创建输出流:`FileOutputStream()`\n2. 创建数据输出流:`DataOutputStream()`\n3. - 写入布尔值true:`dos.writeBoolean(true)`\n - 写入整数:`dos.writeInt(123)`\n - 写入字符串:`dos.writeUTF(\"This is my string\")`\n\n```java\nFile f =new File(\"d:/lol.txt\");\ntry (\n FileOutputStream fos = new FileOutputStream(f);\n DataOutputStream dos =new DataOutputStream(fos);\n){\n dos.writeBoolean(true);\n dos.writeInt(300);\n dos.writeUTF(\"123 this is gareen\");\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n\n\n## 对象流\n\n### 序列化一个对象\n\n需要用到:\n\n- 对象输入流:`ObjectInputStream`\n- 对象输出流:`ObjectOutputStream`\n\n> 把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口\n\n```java\npackage charactor;\n \nimport java.io.Serializable;\n \npublic class Hero implements Serializable {\n //表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号\n private static final long serialVersionUID = 1L;\n public String name;\n public float hp;\n \n}\n```\n\n步骤:\n\n1. 创建一个Hero对象h,设置其名称为garen。\n\n```java\nHero h = new Hero();\nh.name = \"garen\";\nh.hp = 616;\n```\n\n2. 把该对象序列化(即写入)到一个文件`garen.lol`。\n\n```java\nFile f =new File(\"d:/garen.lol\");\n\ntry(\n//创建对象输出流\nFileOutputStream fos = new FileOutputStream(f);\nObjectOutputStream oos =new ObjectOutputStream(fos);\n\n) {\noos.writeObject(h);\n\n} catch (IOException e) {\n// TODO Auto-generated catch block\ne.printStackTrace();\n} \n```\n\n3. 然后再通过序列化把该文件转换为一个Hero对象\n\n```java\nFile f =new File(\"d:/garen.lol\");\n\ntry(\n//创建对象输入流\nFileInputStream fis = new FileInputStream(f);\nObjectInputStream ois =new ObjectInputStream(fis);\n){\n Hero h2 = (Hero) ois.readObject();\n\tSystem.out.println(h2.name);\n\tSystem.out.println(h2.hp);\n}catch (ClassNotFoundException e) {\n// TODO Auto-generated catch block\ne.printStackTrace();\n}\n```\n\n\n\n## System.in\n\n- System.out 是常用的在控制台输出数据的\n- System.in 可以从控制台输入数据\n\n```java\ntry (InputStream is = System.in;) {\n while (true) {\n // 敲入a,然后敲回车可以看到\n // 97 13 10\n // 97是a的ASCII码\n // 13 10分别对应回车换行\n int i = is.read();\n System.out.println(i);\n }\n} catch (IOException e) {\n e.printStackTrace();\n}\n\n```\n\n### Scanner读取字符串\n\n使用System.in.read虽然可以读取数据,但是很不方便;\n使用`Scanner`就可以**逐行**读取了\n\n```java\nimport java.util.Scanner; //使用前需要导入\n```\n\n\n\n```java\nScanner s = new Scanner(System.in);\n\nwhile(true){\n String line = s.nextLine();\n System.out.println(line);\n}\n```\n\n### Scanner从控制台读取整数\n\n```java\nScanner s = new Scanner(System.in);\nint a = s.nextInt();\nSystem.out.println(\"第一个整数:\" + a);\nint b = s.nextInt();\nSystem.out.println(\"第二个整数:\" + b);\n```\n\n\n\n\n\n\n\n\n\n\n\n","source":"_posts/javaSE/16-文件IO.md","raw":"---\ntitle: 16-文件IO\ntags:\n - javaSE\ncategories:\n - - java\n - javaSE\nabbrlink: 4f0e7b50\ndate: 2021-02-16 10:11:35\ndescription: java中关于文件IO的操作\ncover: https://gitee.com/ajream/images/raw/master/img/20210829233015_java_cover.png\n---\n\n\n\n\n# 文件IO系列\n\n\n\n文件和文件夹都是用`File`代表\n\n\n\n## 文件对象\n\n### 创建一个文件对象\n\n> 注意:不是创建文件\n\n首先导入 `File`类\n\n```java\nimport java.io.File;\n```\n\n使用绝对路径或者相对路径创建File对象\n\n```java\n// 绝对路径\nFile f1 = new File(\"d:/LOLFolder\"); //这是一个文件夹\n\n// 相对路径, 相对于工作目录(项目目录)\nFile f2 = new File(\"LOL.exe\");\t\t//这是一个文件\n```\n\n```java\n// 把f1作为父目录创建文件对象\nFile f3 = new File(f1, \"LOL.exe\");\n```\n\n### 文件常用方法\n\n```java\nFile f = new File(\"d:/LOLFolder/skin/garen.ski\");\n```\n\n\n\n- 获取文件相关信息:\n\n```java\nf.exists();\t\t//判断文件是否存在\n\nf.isDirectory();\t//判断是否是文件夹\n\nf.isFile(); //判断是否是文件\n\nf.length(); //文件长度(单位字节bytes)\n\nlong time = f.lastModified(); //返回从1970-1-1 08:00:00 开始的秒数\nDate d = new Date(time);\n\n\nf.setLastModified(0);\t//设置文件修改时间为1970.1.1 08:00:00\n\n```\n\n\n\n- 文件、文件夹操作\n\n```java\nf.list();\n// 以【字符串数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)\n\nFile[]fs = f.listFiles();\n// 以【文件(后缀为Files)数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)\n\nf.getParent();\n// 以【字符串】形式返回文件所在文件夹\n\nf.getParentFile();\n// 以【文件】形式返回获取所在文件夹\n\nf.mkdir();\n// 创建文件夹,如果父文件夹skin不存在,创建就无效\n\nf.mkdirs();\n// 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹\n\nf.createNewFile();\n// 创建一个空文件,如果父文件夹skin不存在,就会抛出异常\n\nf.getParentFile().mkdirs();\n// 所以创建一个空文件之前,通常都会创建父目录\n\nf.listRoots();\n// 列出所有的盘符c: d: e: 等等\n\nf.delete();\n// 刪除文件\n\nf.deleteOnExit();\n// JVM结束的时候,刪除文件,常用于临时文件的删除\n```\n\n\n\n## 什么是流\n\n> 流(Stream)就是一系列的数据\n\n当不同的介质之间有数据交互的时候,JAVA就使用流来实现。\n数据源可以是**文件**,**数据库**,**网络**,甚至是**其他的程序**。\n\n比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流\n\n(输入输出是针对Java虚拟机JVM而言的,流入JVM叫输入,反之叫输出)\n\n- 输入流:InputStream\n- 输出流:OutputStream\n\n![什么是流](https://stepimagewm.how2j.cn/759.png)\n\n### 文件输入流\n\njava中通过 `FileInputStream()` 实现文件输入流。\n\n如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。\n\n目前代码只是建立了流,还没有开始读取,真正的读取在下个章节讲解。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n try {\n File f = new File(\"d:/lol.txt\");//创建文件对象\n \n // 创建基于文件的输入流,即将文件对象放入流中\n FileInputStream fis = new FileInputStream(f);\n // 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中\n \n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 文件输出流\n\n`FileOutputStream` :通过这个输出流,就可以吧数据从java的虚拟机中写入硬盘\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\n \npublic class TestStream {\n    public static void main(String[] args) {\n        File f =new File(\"d:/lol.txt\");\n        try {\n            FileOutputStream fos = new FileOutputStream(f);\n        } catch (FileNotFoundException e) {\n            e.printStackTrace();\n        }\n \n    }\n}\n```\n\n\n\n## 字节流\n\n字节流即:用于以字节的形式读取和写入数据\n\nInputStream:字节输入流\nOutputStream:字节输出流\n\n### ASCII码概念\n\n所有的数据存放在计算机中都是以数字的形式存放的。 所以**字母就需要转换为数字才能够存放**。\n\n比如A就对应的数字65,a对应的数字97. 不同的**字母和符号**对应不同的数字,就是一张码表。\n\n> ASCII是这样的一种码表。 只包含简单的英文字母、符号、数字等。 **不包含中文,德文,俄语等复杂**的。\n\n### 以字节流形式读取文件内容\n\n`InputStream`是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。\n\n`FileInputStream` 是InputStream 子类,以 FileInputStream 为例进行文件读取\n\n```java\npackage IOlearning.stream;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n\npublic class StreamTest {\n\n public static void main(String[] args) {\n try {\n \n File f = new File(\"d:/javaTest.txt\"); //文件javaTest.txt内容是abc...xyz\n\n // 创建基于文件的输入流\n FileInputStream fis = new FileInputStream(f);\n\n // 创建字节数组,其长度就是文件的长度\n byte[] all = new byte[(int) f.length()];\n \n fis.read(all); // 以字节流的形式读取文件所有内容到 all\n \n for (byte b : all) {\n System.out.print(b + \" \");\n// 打印出来是97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122\n }\n\n // 每次使用完流,都应该进行关闭\n fis.close();\n\n } catch (IOException e) {\n e.printStackTrace();\n }\n\n }\n}\n```\n\n### 以字节流的形式向文件写入数据\n\n`OutputStream` 是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。\n\n`FileOutputStream` 是OutputStream子类,以FileOutputStream 为例向文件写出数据\n\n> **注**: 若文件d:/lol2.txt不存在,写出操作会***自动创建***该文件。\n> 但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n try {\n // 准备文件javaTest2.txt, 其中的内容是空的\n File f = new File(\"d:/javaTest2.txt\");\n \n // 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y\n byte data[] = { 88, 89 };\n \n // 创建基于文件的输出流\n FileOutputStream fos = new FileOutputStream(f);\n \n fos.write(data);// 把数据写入到输出流,注意data是个数组\n \n // 关闭输出流\n fos.close();\n \n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 总结(利用流读取文件步骤)\n\n4个操作步骤:\n\n- 创建文件对象\n\n- 将文件放入对应的流(输入、输出流)\n- 操作流(读取、写入)\n- 关闭流(close)\n\n\n\n## 关闭流的方式\n\n### 在try里关闭\n\n```java\ntry {\n\n File f = new File(\"d:/javaTest.txt\"); \n FileInputStream fis = new FileInputStream(f);\n byte[] all = new byte[(int) f.length()];\n fis.read(all); \n\n //使用完流,进行关闭\n fis.close();\n\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个**弊端**:\n如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 **不推荐**使用\n\n### 在finally里关闭\n\n这是**标准的**关闭流的方式\n\n1. 首先把流的**引用声明**在try的外面,如果声明在try里面,其作用域无法抵达finally.\n\n2. 在finally关闭之前,要先判断该引用是否为空\n\n3. 关闭的时候,需要再一次进行try catch处理\n\n> 这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的[有隐患](#在try里关闭)的方式,因为不麻烦🤣\n\n下面是标准方式:\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:/lol.txt\");\n FileInputStream fis = null;\t\t//在try外部声明\n \n try {\n fis = new FileInputStream(f);\n byte[] all = new byte[(int) f.length()];\n fis.read(all);\n for (byte b : all) {\n System.out.println(b);\n }\n \n } catch (IOException e) {\n e.printStackTrace();\n } finally {\n \n // 在finally 里关闭流\n if (null != fis)\n try {\n \n fis.close();\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n }\n \n }\n}\n```\n\n### 使用try()的方式\n\n把流定义在try()里,当try、catch或者finally结束的时候,会自动关闭。注意区别[在try里关闭](#在try里关闭)\n\n这种编写代码的方式叫做 **try-with-resources**, 这是从JDK7开始支持的技术。\n\n> 所有的流,都实现了一个**接口**叫做 `AutoCloseable`,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:/lol.txt\");\n \n //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭\n try (FileInputStream fis = new FileInputStream(f)) {\n \n byte[] all = new byte[(int) f.length()];\n fis.read(all);\n for (byte b : all) {\n System.out.println(b);\n }\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n## 字符流\n\n上面所讲的 `InputStream` 与 `OutputStream` 是字节流;\n\n这里还有字符流,即专门用于以字符的形式读写数据:\n\n- Reader\n- Writer\n\n### 用字符流读取文件\n\n`FileReader` 是 Reader子类,以FileReader 为例进行文件读取\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n \n // 准备文件lol.txt其中的内容是AB\n File f = new File(\"d:/lol.txt\");\n \n // 创建基于文件的Reader\n try (FileReader fr = new FileReader(f)) {\n \n // 创建字符数组,其长度就是文件的长度\n char[] all = new char[(int) f.length()];\n \n // 以字符流的形式读取文件所有内容\n fr.read(all);\n \n for (char b : all) {\n // 打印出来是A B\n System.out.println(b);\n }\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### 用字符流把字符串写入文件\n\n`FileWriter` 是Writer的子类,以FileWriter 为例把字符串写入到文件\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n // 准备文件lol2.txt\n File f = new File(\"d:/lol2.txt\");\n // 创建基于文件的Writer\n try (FileWriter fr = new FileWriter(f)) {\n \n // 以字符流的形式把数据写入到文件中\n String data=\"abcdefg1234567890\";\n char[] cs = data.toCharArray();\n fr.write(cs);\n \n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n## 中文问题\n\n### 编码概念\n\nASCII 字符集只有256个字符,用 0-255 之间的数字来表示。包括大小写字母、数字以及少数特殊字符:如标点符号、货币符号等。\n\n对于大多数拉丁语言来说,这些字符已经够用。\n\n但是,许多亚洲和东方语言所用的字符远远不止256个字符。有些超过千个。\n\n因此,为了突破 ASCII 码字符数的限制,试图用新的编码方法来针对超过256个字符的语言编写计算机程序\n\n### 常见编码\n\n工作后经常接触的编码方式有如下几种:\n\n- ISO-8859-1/ASCII: 数字和西欧字母\n- GBK/GB2312/BIG5: 中文\n- UNICODE: 统一码,万国码\n\n其中\n\n- ISO-8859-1 包含 ASCII\n- GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。\n- UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中\n\n### UNICODE和UTF\n\n虽然UNICODE可以存储所有字符,但如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。因为1个Unicode字符就占用`4 bytes`\n\n倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间\n\n在这种情况下,就出现了UNICODE的各种**减肥**子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了**减肥还能保证健康**的效果\n\nUTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的**减肥效果**,一般说来UTF-8是比较常用的方式\n\nUTF-8,UTF-16和UTF-32 彼此的区别在此不作赘述,有兴趣的可以参考 [unicode-百度百科](http://baike.baidu.com/link?url=ty4mEX5hSfK2xAyPO8N2zgxTibBE59CShSb5yFxbVkBun_QVz65llOPEXOepgPeqe3AQDLt6LLjTayn6tioS4_#4)\n\n> UTF-8编码方式:数字和字母用一个字节, 汉字用3个字节。\n\n### Java采用的是Unicode\n\n写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。\n而这些中文字符采用的编码方式,都是使用UNICODE.\n\n例如: \"中\"字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。\n\n### 一个汉字使用不同编码方式的表现\n\n以字符 **中** 为例,查看其在不同编码方式下的值是多少\n\n```java\npackage stream;\n \nimport java.io.UnsupportedEncodingException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n String str = \"中\";\n showCode(str);\n }\n \n private static void showCode(String str) {\n String[] encodes = { \"BIG5\", \"GBK\", \"GB2312\", \"UTF-8\", \"UTF-16\", \"UTF-32\" };\n for (String encode : encodes) {\n showCode(str, encode);\n }\n \n }\n \n private static void showCode(String str, String encode) {\n try {\n System.out.printf(\"字符: \\\"%s\\\" 的在编码方式%s下的十六进制值是%n\", str, encode);\n byte[] bs = str.getBytes(encode);\n \n for (byte b : bs) {\n int i = b&0xff;\n System.out.print(Integer.toHexString(i) + \"\\t\");\n }\n System.out.println();\n System.out.println();\n } catch (UnsupportedEncodingException e) {\n System.out.printf(\"UnsupportedEncodingException: %s编码方式无法解析字符%s\\n\", encode, str);\n }\n }\n}\n```\n\n![image-20210210233822081](https://gitee.com/Dream-and-dreaming/images/raw/master/img/image-20210210233822081.png)\n\n### 用FileInputStream 字节流正确读取中文\n\n为了能够正确的读取中文内容\n\n1. 必须了解文本是以哪种编码方式保存字符的\n\n2. 使用字节流读取文本\n3. 使用对应的**编码方式去识别这些数字**,得到正确的字符。 \n\n如本例,一个文件中的内容是字符\"中\",编码方式是GBK,那么读出来的数据一定是D6D0。\n\n再使用GBK编码方式识别D6D0,就能正确的得到字符**中**\n\n> **注:** 在GBK的棋盘上找到的**中**字后,JVM会自动找到**中**在UNICODE这个棋盘上对应的数字,并且以[UNICODE上的数字保存在内存中](https://how2j.cn/k/io/io-encoding/695.html#step2486)。\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n File f = new File(\"d:\\\\project\\\\j2se\\\\src\\\\test.txt\"); //该文件保存时的编码方式为GBK\n \n try (FileInputStream fis = new FileInputStream(f);) {\n \n byte[] all = new byte[(int) f.length()];\n \n fis.read(all);\t//使用字节流读取文本\n \n String str = new String(all,\"GBK\"); //解码,解码方式为GBK\n System.out.println(str);\n } catch (IOException e) {\n e.printStackTrace();\n }\n \n }\n}\n```\n\n### 用FileReader 字符流正确读取中文\n\nFileReader得到的是字符,所以一定是已经把字节**根据某种编码识别成为字符**了\n\n而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK\n\nFileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:\n\n```java\nnew InputStreamReader(new FileInputStream(f), Charset.forName(\"UTF-8\")); \n```\n\n\n\n## 缓存流\n\n以介质是硬盘为例,**字节流和字符流的弊端**:\n在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。\n\n为了解决以上弊端,采用缓存流。\n缓存流在读取的时候,**会一次性读较多的数据到缓存中**,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。\n\n就好比吃饭,**不用缓存就是每吃一口都到锅里去铲**。**用缓存就是先把饭盛到碗里**,碗里的吃完了,再到锅里去铲。\n\n缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区**达到一定的量**,才把这些数据,**一起写入到硬盘中去**。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作,提高速度。\n\n### 使用缓存流读取数据\n\n缓存字符输入流 `BufferedReader` 可以一次读取一行数据,但要注意,缓存流必须建立在一个存在的流的基础上\n\n> 先准备好文件 `d:/lol.txt`,文件内容如下:\n>\n> garen kill teemo\n> teemo revive after 1 minutes\n> teemo try to garen, but killed again\n\n```java\npackage stream;\n \nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n\n File f = new File(\"d:/lol.txt\");\n \n try (\n FileReader fr = new FileReader(f);\t\t// 创建文件字符流\n BufferedReader br = new BufferedReader(fr); // 缓存流必须建立在一个存在的流的基础上\n )\n {\n while (true) {\n String line = br.readLine();// 一次读一行\n if (line == null)\n break;\n System.out.println(line);\n }\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### 使用缓存流写入数据\n\n[之前](#以字节流的形式向文件写入数据)的 `FileOutputStream` 与 `FileWriter`要写入一串数据时,必须将数据转换为数组,一次只能写入一个字符。\n\n而`PrintWriter` 缓存字符输出流, 可以一次写入一行数据;\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.PrintWriter;\n \npublic class TestStream {\n \n public static void main(String[] args) {\n // 向文件lol2.txt中写入三行语句\n File f = new File(\"d:/lol2.txt\");\n \n try (\n FileWriter fw = new FileWriter(f); // 创建文件字符流\n PrintWriter pw = new PrintWriter(fw); // 缓存流必须建立在一个存在的流的基础上 \n \n ) {\n pw.println(\"garen kill teemo\"); //写入一行数据\n pw.println(\"teemo revive after 1 minutes\");\n pw.println(\"teemo try to garen, but killed again\");\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n \n }\n}\n```\n\n\n\n### flush方法\n\n有的时候,需要**立即把数据写入到硬盘**,而不是等缓存满了才写出去。 这时候就需要用到`flush()`方法\n\n```java\npackage stream;\n \nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.PrintWriter;\npublic class TestStream {\n public static void main(String[] args) {\n //向文件lol2.txt中写入三行语句\n File f =new File(\"d:/lol2.txt\");\n \n try(\n FileWriter fr = new FileWriter(f); //创建文件字符流\n PrintWriter pw = new PrintWriter(fr); //缓存流必须建立在一个存在的流的基础上\n ) {\n pw.println(\"garen kill teemo\");\n pw.flush(); //强制把缓存中的数据写入硬盘,无论缓存是否已满\n pw.println(\"teemo revive after 1 minutes\");\n pw.flush();\n pw.println(\"teemo try to garen, but killed again\");\n pw.flush();\n } catch (IOException e) {\n // TODO Auto-generated catch block\n e.printStackTrace();\n }\n }\n}\n```\n\n\n\n## 数据流\n\n- DataInputStream 数据输入流\n- DataOutputStream 数据输出流\n\n### 直接读写字符串\n\n使用数据流的`writeUTF()`和`readUTF()` 可以进行数据的**格式化顺序读写**;\n如本例,通过DataOutputStream 向文件顺序写出【布尔值,整数和字符串】。 然后再通过DataInputStream 顺序读入这些数据。\n\n> **注:** 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException\n>\n> 因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。\n\n读取步骤:\n\n1. 创建输入流 `FileInputStream()`\n2. 创建数据输入流 `DataInputStream()`\n3. - 读取布尔值:`boolean b= dis.readBoolean();`\n - 读取整数:`int i = dis.readInt();`\n - 读取字符串:`String str = dis.readUTF();`\n\n```java\nFile f =new File(\"d:/lol.txt\");\ntry (\n FileInputStream fis = new FileInputStream(f);\n DataInputStream dis =new DataInputStream(fis);\n){\n boolean b= dis.readBoolean();\n int i = dis.readInt();\n String str = dis.readUTF();\n\n System.out.println(\"读取到布尔值:\"+b);\n System.out.println(\"读取到整数:\"+i);\n System.out.println(\"读取到字符串:\"+str);\n\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n写入步骤:\n\n1. 创建输出流:`FileOutputStream()`\n2. 创建数据输出流:`DataOutputStream()`\n3. - 写入布尔值true:`dos.writeBoolean(true)`\n - 写入整数:`dos.writeInt(123)`\n - 写入字符串:`dos.writeUTF(\"This is my string\")`\n\n```java\nFile f =new File(\"d:/lol.txt\");\ntry (\n FileOutputStream fos = new FileOutputStream(f);\n DataOutputStream dos =new DataOutputStream(fos);\n){\n dos.writeBoolean(true);\n dos.writeInt(300);\n dos.writeUTF(\"123 this is gareen\");\n} catch (IOException e) {\n e.printStackTrace();\n}\n```\n\n\n\n## 对象流\n\n### 序列化一个对象\n\n需要用到:\n\n- 对象输入流:`ObjectInputStream`\n- 对象输出流:`ObjectOutputStream`\n\n> 把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口\n\n```java\npackage charactor;\n \nimport java.io.Serializable;\n \npublic class Hero implements Serializable {\n //表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号\n private static final long serialVersionUID = 1L;\n public String name;\n public float hp;\n \n}\n```\n\n步骤:\n\n1. 创建一个Hero对象h,设置其名称为garen。\n\n```java\nHero h = new Hero();\nh.name = \"garen\";\nh.hp = 616;\n```\n\n2. 把该对象序列化(即写入)到一个文件`garen.lol`。\n\n```java\nFile f =new File(\"d:/garen.lol\");\n\ntry(\n//创建对象输出流\nFileOutputStream fos = new FileOutputStream(f);\nObjectOutputStream oos =new ObjectOutputStream(fos);\n\n) {\noos.writeObject(h);\n\n} catch (IOException e) {\n// TODO Auto-generated catch block\ne.printStackTrace();\n} \n```\n\n3. 然后再通过序列化把该文件转换为一个Hero对象\n\n```java\nFile f =new File(\"d:/garen.lol\");\n\ntry(\n//创建对象输入流\nFileInputStream fis = new FileInputStream(f);\nObjectInputStream ois =new ObjectInputStream(fis);\n){\n Hero h2 = (Hero) ois.readObject();\n\tSystem.out.println(h2.name);\n\tSystem.out.println(h2.hp);\n}catch (ClassNotFoundException e) {\n// TODO Auto-generated catch block\ne.printStackTrace();\n}\n```\n\n\n\n## System.in\n\n- System.out 是常用的在控制台输出数据的\n- System.in 可以从控制台输入数据\n\n```java\ntry (InputStream is = System.in;) {\n while (true) {\n // 敲入a,然后敲回车可以看到\n // 97 13 10\n // 97是a的ASCII码\n // 13 10分别对应回车换行\n int i = is.read();\n System.out.println(i);\n }\n} catch (IOException e) {\n e.printStackTrace();\n}\n\n```\n\n### Scanner读取字符串\n\n使用System.in.read虽然可以读取数据,但是很不方便;\n使用`Scanner`就可以**逐行**读取了\n\n```java\nimport java.util.Scanner; //使用前需要导入\n```\n\n\n\n```java\nScanner s = new Scanner(System.in);\n\nwhile(true){\n String line = s.nextLine();\n System.out.println(line);\n}\n```\n\n### Scanner从控制台读取整数\n\n```java\nScanner s = new Scanner(System.in);\nint a = s.nextInt();\nSystem.out.println(\"第一个整数:\" + a);\nint b = s.nextInt();\nSystem.out.println(\"第二个整数:\" + b);\n```\n\n\n\n\n\n\n\n\n\n\n\n","slug":"javaSE/16-文件IO","published":1,"updated":"2021-08-29T15:31:42.266Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cktk8o6tx00hbakve9wq17mn4","content":"

文件IO系列

文件和文件夹都是用File代表

\n

文件对象

创建一个文件对象

\n

注意:不是创建文件

\n
\n

首先导入 File

\n
1
import java.io.File;
\n

使用绝对路径或者相对路径创建File对象

\n
1
2
3
4
5
// 绝对路径
File f1 = new File("d:/LOLFolder"); //这是一个文件夹

// 相对路径, 相对于工作目录(项目目录)
File f2 = new File("LOL.exe");\t\t//这是一个文件
\n
1
2
// 把f1作为父目录创建文件对象
File f3 = new File(f1, "LOL.exe");
\n

文件常用方法

1
File f = new File("d:/LOLFolder/skin/garen.ski");
\n
    \n
  • 获取文件相关信息:
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
f.exists();\t\t//判断文件是否存在

f.isDirectory();\t//判断是否是文件夹

f.isFile(); //判断是否是文件

f.length(); //文件长度(单位字节bytes)

long time = f.lastModified(); //返回从1970-1-1 08:00:00 开始的秒数
Date d = new Date(time);


f.setLastModified(0);\t//设置文件修改时间为1970.1.1 08:00:00

\n
    \n
  • 文件、文件夹操作
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
f.list();
// 以【字符串数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

File[]fs = f.listFiles();
// 以【文件(后缀为Files)数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

f.getParent();
// 以【字符串】形式返回文件所在文件夹

f.getParentFile();
// 以【文件】形式返回获取所在文件夹

f.mkdir();
// 创建文件夹,如果父文件夹skin不存在,创建就无效

f.mkdirs();
// 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹

f.createNewFile();
// 创建一个空文件,如果父文件夹skin不存在,就会抛出异常

f.getParentFile().mkdirs();
// 所以创建一个空文件之前,通常都会创建父目录

f.listRoots();
// 列出所有的盘符c: d: e: 等等

f.delete();
// 刪除文件

f.deleteOnExit();
// JVM结束的时候,刪除文件,常用于临时文件的删除
\n

什么是流

\n

流(Stream)就是一系列的数据

\n
\n

当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
数据源可以是文件数据库网络,甚至是其他的程序

\n

比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流

\n

(输入输出是针对Java虚拟机JVM而言的,流入JVM叫输入,反之叫输出)

\n
    \n
  • 输入流:InputStream
  • \n
  • 输出流:OutputStream
  • \n
\n

\"什么是流\"

\n

文件输入流

java中通过 FileInputStream() 实现文件输入流。

\n

如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。

\n

目前代码只是建立了流,还没有开始读取,真正的读取在下个章节讲解。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
try {
File f = new File("d:/lol.txt");//创建文件对象

// 创建基于文件的输入流,即将文件对象放入流中
FileInputStream fis = new FileInputStream(f);
// 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

文件输出流

FileOutputStream :通过这个输出流,就可以吧数据从java的虚拟机中写入硬盘

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package stream;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
 
public class TestStream {
    public static void main(String[] args) {
        File f =new File("d:/lol.txt");
        try {
            FileOutputStream fos = new FileOutputStream(f);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
 
    }
}
\n

字节流

字节流即:用于以字节的形式读取和写入数据

\n

InputStream:字节输入流
OutputStream:字节输出流

\n

ASCII码概念

所有的数据存放在计算机中都是以数字的形式存放的。 所以字母就需要转换为数字才能够存放

\n

比如A就对应的数字65,a对应的数字97. 不同的字母和符号对应不同的数字,就是一张码表。

\n
\n

ASCII是这样的一种码表。 只包含简单的英文字母、符号、数字等。 不包含中文,德文,俄语等复杂的。

\n
\n

以字节流形式读取文件内容

InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。

\n

FileInputStream 是InputStream 子类,以 FileInputStream 为例进行文件读取

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package IOlearning.stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class StreamTest {

public static void main(String[] args) {
try {

File f = new File("d:/javaTest.txt"); //文件javaTest.txt内容是abc...xyz

// 创建基于文件的输入流
FileInputStream fis = new FileInputStream(f);

// 创建字节数组,其长度就是文件的长度
byte[] all = new byte[(int) f.length()];

fis.read(all); // 以字节流的形式读取文件所有内容到 all

for (byte b : all) {
System.out.print(b + " ");
// 打印出来是97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
}

// 每次使用完流,都应该进行关闭
fis.close();

} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

以字节流的形式向文件写入数据

OutputStream 是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。

\n

FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据

\n
\n

: 若文件d:/lol2.txt不存在,写出操作会自动创建该文件。
但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package stream;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
try {
// 准备文件javaTest2.txt, 其中的内容是空的
File f = new File("d:/javaTest2.txt");

// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
byte data[] = { 88, 89 };

// 创建基于文件的输出流
FileOutputStream fos = new FileOutputStream(f);

fos.write(data);// 把数据写入到输出流,注意data是个数组

// 关闭输出流
fos.close();

} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

总结(利用流读取文件步骤)

4个操作步骤:

\n
    \n
  • 创建文件对象

    \n
  • \n
  • 将文件放入对应的流(输入、输出流)

    \n
  • \n
  • 操作流(读取、写入)
  • \n
  • 关闭流(close)
  • \n
\n

关闭流的方式

在try里关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
try {

File f = new File("d:/javaTest.txt");
FileInputStream fis = new FileInputStream(f);
byte[] all = new byte[(int) f.length()];
fis.read(all);

//使用完流,进行关闭
fis.close();

} catch (IOException e) {
e.printStackTrace();
}
\n

在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用

\n

在finally里关闭

这是标准的关闭流的方式

\n
    \n
  1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.

    \n
  2. \n
  3. 在finally关闭之前,要先判断该引用是否为空

    \n
  4. \n
  5. 关闭的时候,需要再一次进行try catch处理

    \n
  6. \n
\n
\n

这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患的方式,因为不麻烦🤣

\n
\n

下面是标准方式:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:/lol.txt");
FileInputStream fis = null;\t\t//在try外部声明

try {
fis = new FileInputStream(f);
byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}

} catch (IOException e) {
e.printStackTrace();
} finally {

// 在finally 里关闭流
if (null != fis)
try {

fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
}
\n

使用try()的方式

把流定义在try()里,当try、catch或者finally结束的时候,会自动关闭。注意区别在try里关闭

\n

这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术。

\n
\n

所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:/lol.txt");

//把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
try (FileInputStream fis = new FileInputStream(f)) {

byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

字符流

上面所讲的 InputStreamOutputStream 是字节流;

\n

这里还有字符流,即专门用于以字符的形式读写数据:

\n
    \n
  • Reader
  • \n
  • Writer
  • \n
\n

用字符流读取文件

FileReader 是 Reader子类,以FileReader 为例进行文件读取

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package stream;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {

// 准备文件lol.txt其中的内容是AB
File f = new File("d:/lol.txt");

// 创建基于文件的Reader
try (FileReader fr = new FileReader(f)) {

// 创建字符数组,其长度就是文件的长度
char[] all = new char[(int) f.length()];

// 以字符流的形式读取文件所有内容
fr.read(all);

for (char b : all) {
// 打印出来是A B
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

用字符流把字符串写入文件

FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
// 准备文件lol2.txt
File f = new File("d:/lol2.txt");
// 创建基于文件的Writer
try (FileWriter fr = new FileWriter(f)) {

// 以字符流的形式把数据写入到文件中
String data="abcdefg1234567890";
char[] cs = data.toCharArray();
fr.write(cs);

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

中文问题

编码概念

ASCII 字符集只有256个字符,用 0-255 之间的数字来表示。包括大小写字母、数字以及少数特殊字符:如标点符号、货币符号等。

\n

对于大多数拉丁语言来说,这些字符已经够用。

\n

但是,许多亚洲和东方语言所用的字符远远不止256个字符。有些超过千个。

\n

因此,为了突破 ASCII 码字符数的限制,试图用新的编码方法来针对超过256个字符的语言编写计算机程序

\n

常见编码

工作后经常接触的编码方式有如下几种:

\n
    \n
  • ISO-8859-1/ASCII: 数字和西欧字母
  • \n
  • GBK/GB2312/BIG5: 中文
  • \n
  • UNICODE: 统一码,万国码
  • \n
\n

其中

\n
    \n
  • ISO-8859-1 包含 ASCII
  • \n
  • GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
  • \n
  • UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中
  • \n
\n

UNICODE和UTF

虽然UNICODE可以存储所有字符,但如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。因为1个Unicode字符就占用4 bytes

\n

倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间

\n

在这种情况下,就出现了UNICODE的各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了减肥还能保证健康的效果

\n

UTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的减肥效果,一般说来UTF-8是比较常用的方式

\n

UTF-8,UTF-16和UTF-32 彼此的区别在此不作赘述,有兴趣的可以参考 unicode-百度百科

\n
\n

UTF-8编码方式:数字和字母用一个字节, 汉字用3个字节。

\n
\n

Java采用的是Unicode

写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。
而这些中文字符采用的编码方式,都是使用UNICODE.

\n

例如: “中”字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。

\n

一个汉字使用不同编码方式的表现

以字符 为例,查看其在不同编码方式下的值是多少

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package stream;

import java.io.UnsupportedEncodingException;

public class TestStream {

public static void main(String[] args) {
String str = "中";
showCode(str);
}

private static void showCode(String str) {
String[] encodes = { "BIG5", "GBK", "GB2312", "UTF-8", "UTF-16", "UTF-32" };
for (String encode : encodes) {
showCode(str, encode);
}

}

private static void showCode(String str, String encode) {
try {
System.out.printf("字符: \\"%s\\" 的在编码方式%s下的十六进制值是%n", str, encode);
byte[] bs = str.getBytes(encode);

for (byte b : bs) {
int i = b&0xff;
System.out.print(Integer.toHexString(i) + "\\t");
}
System.out.println();
System.out.println();
} catch (UnsupportedEncodingException e) {
System.out.printf("UnsupportedEncodingException: %s编码方式无法解析字符%s\\n", encode, str);
}
}
}
\n

\"image-20210210233822081\"

\n

用FileInputStream 字节流正确读取中文

为了能够正确的读取中文内容

\n
    \n
  1. 必须了解文本是以哪种编码方式保存字符的

    \n
  2. \n
  3. 使用字节流读取文本

    \n
  4. \n
  5. 使用对应的编码方式去识别这些数字,得到正确的字符。
  6. \n
\n

如本例,一个文件中的内容是字符”中”,编码方式是GBK,那么读出来的数据一定是D6D0。

\n

再使用GBK编码方式识别D6D0,就能正确的得到字符

\n
\n

注: 在GBK的棋盘上找到的字后,JVM会自动找到在UNICODE这个棋盘上对应的数字,并且以UNICODE上的数字保存在内存中

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:\\\\project\\\\j2se\\\\src\\\\test.txt"); //该文件保存时的编码方式为GBK

try (FileInputStream fis = new FileInputStream(f);) {

byte[] all = new byte[(int) f.length()];

fis.read(all);\t//使用字节流读取文本

String str = new String(all,"GBK"); //解码,解码方式为GBK
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

用FileReader 字符流正确读取中文

FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成为字符

\n

而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK

\n

FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:

\n
1
new InputStreamReader(new FileInputStream(f), Charset.forName("UTF-8")); 
\n

缓存流

以介质是硬盘为例,字节流和字符流的弊端
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。

\n

为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。

\n

就好比吃饭,不用缓存就是每吃一口都到锅里去铲用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲。

\n

缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作,提高速度。

\n

使用缓存流读取数据

缓存字符输入流 BufferedReader 可以一次读取一行数据,但要注意,缓存流必须建立在一个存在的流的基础上

\n
\n

先准备好文件 d:/lol.txt,文件内容如下:

\n

garen kill teemo
teemo revive after 1 minutes
teemo try to garen, but killed again

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package stream;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {

File f = new File("d:/lol.txt");

try (
FileReader fr = new FileReader(f);\t\t// 创建文件字符流
BufferedReader br = new BufferedReader(fr); // 缓存流必须建立在一个存在的流的基础上
)
{
while (true) {
String line = br.readLine();// 一次读一行
if (line == null)
break;
System.out.println(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

使用缓存流写入数据

之前FileOutputStreamFileWriter要写入一串数据时,必须将数据转换为数组,一次只能写入一个字符。

\n

PrintWriter 缓存字符输出流, 可以一次写入一行数据;

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class TestStream {

public static void main(String[] args) {
// 向文件lol2.txt中写入三行语句
File f = new File("d:/lol2.txt");

try (
FileWriter fw = new FileWriter(f); // 创建文件字符流
PrintWriter pw = new PrintWriter(fw); // 缓存流必须建立在一个存在的流的基础上

) {
pw.println("garen kill teemo"); //写入一行数据
pw.println("teemo revive after 1 minutes");
pw.println("teemo try to garen, but killed again");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

flush方法

有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush()方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class TestStream {
public static void main(String[] args) {
//向文件lol2.txt中写入三行语句
File f =new File("d:/lol2.txt");

try(
FileWriter fr = new FileWriter(f); //创建文件字符流
PrintWriter pw = new PrintWriter(fr); //缓存流必须建立在一个存在的流的基础上
) {
pw.println("garen kill teemo");
pw.flush(); //强制把缓存中的数据写入硬盘,无论缓存是否已满
pw.println("teemo revive after 1 minutes");
pw.flush();
pw.println("teemo try to garen, but killed again");
pw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
\n

数据流

    \n
  • DataInputStream 数据输入流
  • \n
  • DataOutputStream 数据输出流
  • \n
\n

直接读写字符串

使用数据流的writeUTF()readUTF() 可以进行数据的格式化顺序读写
如本例,通过DataOutputStream 向文件顺序写出【布尔值,整数和字符串】。 然后再通过DataInputStream 顺序读入这些数据。

\n
\n

注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException

\n

因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。

\n
\n

读取步骤:

\n
    \n
  1. 创建输入流 FileInputStream()
  2. \n
  3. 创建数据输入流 DataInputStream()
  4. \n
    • \n
    • 读取布尔值:boolean b= dis.readBoolean();
    • \n
    • 读取整数:int i = dis.readInt();
    • \n
    • 读取字符串:String str = dis.readUTF();
    • \n
    \n
  5. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
File f =new File("d:/lol.txt");
try (
FileInputStream fis = new FileInputStream(f);
DataInputStream dis =new DataInputStream(fis);
){
boolean b= dis.readBoolean();
int i = dis.readInt();
String str = dis.readUTF();

System.out.println("读取到布尔值:"+b);
System.out.println("读取到整数:"+i);
System.out.println("读取到字符串:"+str);

} catch (IOException e) {
e.printStackTrace();
}
\n

写入步骤:

\n
    \n
  1. 创建输出流:FileOutputStream()
  2. \n
  3. 创建数据输出流:DataOutputStream()
  4. \n
    • \n
    • 写入布尔值true:dos.writeBoolean(true)
    • \n
    • 写入整数:dos.writeInt(123)
    • \n
    • 写入字符串:dos.writeUTF("This is my string")
    • \n
    \n
  5. \n
\n
1
2
3
4
5
6
7
8
9
10
11
File f =new File("d:/lol.txt");
try (
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos =new DataOutputStream(fos);
){
dos.writeBoolean(true);
dos.writeInt(300);
dos.writeUTF("123 this is gareen");
} catch (IOException e) {
e.printStackTrace();
}
\n

对象流

序列化一个对象

需要用到:

\n
    \n
  • 对象输入流:ObjectInputStream
  • \n
  • 对象输出流:ObjectOutputStream
  • \n
\n
\n

把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口

\n
\n
1
2
3
4
5
6
7
8
9
10
11
package charactor;

import java.io.Serializable;

public class Hero implements Serializable {
//表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
private static final long serialVersionUID = 1L;
public String name;
public float hp;

}
\n

步骤:

\n
    \n
  1. 创建一个Hero对象h,设置其名称为garen。
  2. \n
\n
1
2
3
Hero h = new Hero();
h.name = "garen";
h.hp = 616;
\n
    \n
  1. 把该对象序列化(即写入)到一个文件garen.lol
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
File f =new File("d:/garen.lol");

try(
//创建对象输出流
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos =new ObjectOutputStream(fos);

) {
oos.writeObject(h);

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
\n
    \n
  1. 然后再通过序列化把该文件转换为一个Hero对象
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
File f =new File("d:/garen.lol");

try(
//创建对象输入流
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois =new ObjectInputStream(fis);
){
Hero h2 = (Hero) ois.readObject();
\tSystem.out.println(h2.name);
\tSystem.out.println(h2.hp);
}catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
\n

System.in

    \n
  • System.out 是常用的在控制台输出数据的
  • \n
  • System.in 可以从控制台输入数据
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
try (InputStream is = System.in;) {
while (true) {
// 敲入a,然后敲回车可以看到
// 97 13 10
// 97是a的ASCII码
// 13 10分别对应回车换行
int i = is.read();
System.out.println(i);
}
} catch (IOException e) {
e.printStackTrace();
}

\n

Scanner读取字符串

使用System.in.read虽然可以读取数据,但是很不方便;
使用Scanner就可以逐行读取了

\n
1
import java.util.Scanner;  //使用前需要导入
\n
1
2
3
4
5
6
Scanner s = new Scanner(System.in);

while(true){
String line = s.nextLine();
System.out.println(line);
}
\n

Scanner从控制台读取整数

1
2
3
4
5
Scanner s = new Scanner(System.in);
int a = s.nextInt();
System.out.println("第一个整数:" + a);
int b = s.nextInt();
System.out.println("第二个整数:" + b);
\n","site":{"data":{"link":[{"class_name":"小伙伴","class_desc":"各路小伙伴的博客网站","link_list":[{"name":"小康博客","link":"https://www.antmoe.com/","avatar":"https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/avatar.jpg","descr":"一个收藏回忆与分享技术的地方!"},{"name":"小嘉的部落格","link":"https://blog.imzjw.cn","avatar":"https://cdn.jsdelivr.net/npm/nanshen/avatar.jpg","descr":"一个爱折腾的Java开发工程师"},{"name":"小冰博客","link":"https://zfe.space/","avatar":"https://zfe.space/images/headimage.png","descr":"做个有梦想的人!"},{"name":"Akilarの糖果屋","link":"https://akilar.top/","avatar":"https://akilar.top/img/siteicon/favicon.png","descr":"期待您的光临!"},{"name":"guole's Blog","link":"https://guole.fun/","avatar":"https://guole.fun/img/gl.jpg","descr":"保持理智,相信明天。"}]},{"class_name":"网站","class_desc":"值得推荐的网站","link_list":[{"name":"bilibili","link":"https://www.bilibili.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816082718_Bilibili.png","descr":"视频分享平台"},{"name":"知乎","link":"https://www.zhihu.com/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083527_知乎 .png","descr":"中文互联网高质量问答社区"},{"name":"CSDN","link":"https://www.csdn.net/","avatar":"https://gitee.com/ajream/images/raw/master/img/20210816083817_CSDN.png","descr":"中国专业IT社区"},{"name":"Youtube","link":"https://www.youtube.com/","avatar":"https://i.loli.net/2020/05/14/9ZkGg8v3azHJfM1.png","descr":"国外视频网站"},{"name":"Weibo","link":"https://www.weibo.com/","avatar":"https://i.loli.net/2020/05/14/TLJBum386vcnI1P.png","descr":"中国最大社交分享平台"},{"name":"Twitter","link":"https://twitter.com/","avatar":"https://i.loli.net/2020/05/14/5VyHPQqR6LWF39a.png","descr":"社交分享平台"}]}]}},"excerpt":"","more":"

文件IO系列

文件和文件夹都是用File代表

\n

文件对象

创建一个文件对象

\n

注意:不是创建文件

\n
\n

首先导入 File

\n
1
import java.io.File;
\n

使用绝对路径或者相对路径创建File对象

\n
1
2
3
4
5
// 绝对路径
File f1 = new File("d:/LOLFolder"); //这是一个文件夹

// 相对路径, 相对于工作目录(项目目录)
File f2 = new File("LOL.exe");\t\t//这是一个文件
\n
1
2
// 把f1作为父目录创建文件对象
File f3 = new File(f1, "LOL.exe");
\n

文件常用方法

1
File f = new File("d:/LOLFolder/skin/garen.ski");
\n
    \n
  • 获取文件相关信息:
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
f.exists();\t\t//判断文件是否存在

f.isDirectory();\t//判断是否是文件夹

f.isFile(); //判断是否是文件

f.length(); //文件长度(单位字节bytes)

long time = f.lastModified(); //返回从1970-1-1 08:00:00 开始的秒数
Date d = new Date(time);


f.setLastModified(0);\t//设置文件修改时间为1970.1.1 08:00:00

\n
    \n
  • 文件、文件夹操作
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
f.list();
// 以【字符串数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

File[]fs = f.listFiles();
// 以【文件(后缀为Files)数组】的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

f.getParent();
// 以【字符串】形式返回文件所在文件夹

f.getParentFile();
// 以【文件】形式返回获取所在文件夹

f.mkdir();
// 创建文件夹,如果父文件夹skin不存在,创建就无效

f.mkdirs();
// 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹

f.createNewFile();
// 创建一个空文件,如果父文件夹skin不存在,就会抛出异常

f.getParentFile().mkdirs();
// 所以创建一个空文件之前,通常都会创建父目录

f.listRoots();
// 列出所有的盘符c: d: e: 等等

f.delete();
// 刪除文件

f.deleteOnExit();
// JVM结束的时候,刪除文件,常用于临时文件的删除
\n

什么是流

\n

流(Stream)就是一系列的数据

\n
\n

当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
数据源可以是文件数据库网络,甚至是其他的程序

\n

比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流

\n

(输入输出是针对Java虚拟机JVM而言的,流入JVM叫输入,反之叫输出)

\n
    \n
  • 输入流:InputStream
  • \n
  • 输出流:OutputStream
  • \n
\n

\"什么是流\"

\n

文件输入流

java中通过 FileInputStream() 实现文件输入流。

\n

如下代码,就建立了一个文件输入流,这个流可以用来把数据从硬盘的文件,读取到JVM(内存)。

\n

目前代码只是建立了流,还没有开始读取,真正的读取在下个章节讲解。

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
try {
File f = new File("d:/lol.txt");//创建文件对象

// 创建基于文件的输入流,即将文件对象放入流中
FileInputStream fis = new FileInputStream(f);
// 通过这个输入流,就可以把数据从硬盘,读取到Java的虚拟机中来,也就是读取到内存中

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

文件输出流

FileOutputStream :通过这个输出流,就可以吧数据从java的虚拟机中写入硬盘

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package stream;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
 
public class TestStream {
    public static void main(String[] args) {
        File f =new File("d:/lol.txt");
        try {
            FileOutputStream fos = new FileOutputStream(f);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
 
    }
}
\n

字节流

字节流即:用于以字节的形式读取和写入数据

\n

InputStream:字节输入流
OutputStream:字节输出流

\n

ASCII码概念

所有的数据存放在计算机中都是以数字的形式存放的。 所以字母就需要转换为数字才能够存放

\n

比如A就对应的数字65,a对应的数字97. 不同的字母和符号对应不同的数字,就是一张码表。

\n
\n

ASCII是这样的一种码表。 只包含简单的英文字母、符号、数字等。 不包含中文,德文,俄语等复杂的。

\n
\n

以字节流形式读取文件内容

InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。

\n

FileInputStream 是InputStream 子类,以 FileInputStream 为例进行文件读取

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package IOlearning.stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class StreamTest {

public static void main(String[] args) {
try {

File f = new File("d:/javaTest.txt"); //文件javaTest.txt内容是abc...xyz

// 创建基于文件的输入流
FileInputStream fis = new FileInputStream(f);

// 创建字节数组,其长度就是文件的长度
byte[] all = new byte[(int) f.length()];

fis.read(all); // 以字节流的形式读取文件所有内容到 all

for (byte b : all) {
System.out.print(b + " ");
// 打印出来是97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
}

// 每次使用完流,都应该进行关闭
fis.close();

} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

以字节流的形式向文件写入数据

OutputStream 是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。

\n

FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据

\n
\n

: 若文件d:/lol2.txt不存在,写出操作会自动创建该文件。
但是如果是文件 d:/xyz/lol2.txt,而目录xyz又不存在,会抛出异常

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package stream;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
try {
// 准备文件javaTest2.txt, 其中的内容是空的
File f = new File("d:/javaTest2.txt");

// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
byte data[] = { 88, 89 };

// 创建基于文件的输出流
FileOutputStream fos = new FileOutputStream(f);

fos.write(data);// 把数据写入到输出流,注意data是个数组

// 关闭输出流
fos.close();

} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

总结(利用流读取文件步骤)

4个操作步骤:

\n
    \n
  • 创建文件对象

    \n
  • \n
  • 将文件放入对应的流(输入、输出流)

    \n
  • \n
  • 操作流(读取、写入)
  • \n
  • 关闭流(close)
  • \n
\n

关闭流的方式

在try里关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
try {

File f = new File("d:/javaTest.txt");
FileInputStream fis = new FileInputStream(f);
byte[] all = new byte[(int) f.length()];
fis.read(all);

//使用完流,进行关闭
fis.close();

} catch (IOException e) {
e.printStackTrace();
}
\n

在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用

\n

在finally里关闭

这是标准的关闭流的方式

\n
    \n
  1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.

    \n
  2. \n
  3. 在finally关闭之前,要先判断该引用是否为空

    \n
  4. \n
  5. 关闭的时候,需要再一次进行try catch处理

    \n
  6. \n
\n
\n

这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患的方式,因为不麻烦🤣

\n
\n

下面是标准方式:

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:/lol.txt");
FileInputStream fis = null;\t\t//在try外部声明

try {
fis = new FileInputStream(f);
byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}

} catch (IOException e) {
e.printStackTrace();
} finally {

// 在finally 里关闭流
if (null != fis)
try {

fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
}
\n

使用try()的方式

把流定义在try()里,当try、catch或者finally结束的时候,会自动关闭。注意区别在try里关闭

\n

这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术。

\n
\n

所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:/lol.txt");

//把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
try (FileInputStream fis = new FileInputStream(f)) {

byte[] all = new byte[(int) f.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

字符流

上面所讲的 InputStreamOutputStream 是字节流;

\n

这里还有字符流,即专门用于以字符的形式读写数据:

\n
    \n
  • Reader
  • \n
  • Writer
  • \n
\n

用字符流读取文件

FileReader 是 Reader子类,以FileReader 为例进行文件读取

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package stream;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {

// 准备文件lol.txt其中的内容是AB
File f = new File("d:/lol.txt");

// 创建基于文件的Reader
try (FileReader fr = new FileReader(f)) {

// 创建字符数组,其长度就是文件的长度
char[] all = new char[(int) f.length()];

// 以字符流的形式读取文件所有内容
fr.read(all);

for (char b : all) {
// 打印出来是A B
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

用字符流把字符串写入文件

FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
// 准备文件lol2.txt
File f = new File("d:/lol2.txt");
// 创建基于文件的Writer
try (FileWriter fr = new FileWriter(f)) {

// 以字符流的形式把数据写入到文件中
String data="abcdefg1234567890";
char[] cs = data.toCharArray();
fr.write(cs);

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

中文问题

编码概念

ASCII 字符集只有256个字符,用 0-255 之间的数字来表示。包括大小写字母、数字以及少数特殊字符:如标点符号、货币符号等。

\n

对于大多数拉丁语言来说,这些字符已经够用。

\n

但是,许多亚洲和东方语言所用的字符远远不止256个字符。有些超过千个。

\n

因此,为了突破 ASCII 码字符数的限制,试图用新的编码方法来针对超过256个字符的语言编写计算机程序

\n

常见编码

工作后经常接触的编码方式有如下几种:

\n
    \n
  • ISO-8859-1/ASCII: 数字和西欧字母
  • \n
  • GBK/GB2312/BIG5: 中文
  • \n
  • UNICODE: 统一码,万国码
  • \n
\n

其中

\n
    \n
  • ISO-8859-1 包含 ASCII
  • \n
  • GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
  • \n
  • UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中
  • \n
\n

UNICODE和UTF

虽然UNICODE可以存储所有字符,但如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。因为1个Unicode字符就占用4 bytes

\n

倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间

\n

在这种情况下,就出现了UNICODE的各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了减肥还能保证健康的效果

\n

UTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的减肥效果,一般说来UTF-8是比较常用的方式

\n

UTF-8,UTF-16和UTF-32 彼此的区别在此不作赘述,有兴趣的可以参考 unicode-百度百科

\n
\n

UTF-8编码方式:数字和字母用一个字节, 汉字用3个字节。

\n
\n

Java采用的是Unicode

写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。
而这些中文字符采用的编码方式,都是使用UNICODE.

\n

例如: “中”字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。

\n

一个汉字使用不同编码方式的表现

以字符 为例,查看其在不同编码方式下的值是多少

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package stream;

import java.io.UnsupportedEncodingException;

public class TestStream {

public static void main(String[] args) {
String str = "中";
showCode(str);
}

private static void showCode(String str) {
String[] encodes = { "BIG5", "GBK", "GB2312", "UTF-8", "UTF-16", "UTF-32" };
for (String encode : encodes) {
showCode(str, encode);
}

}

private static void showCode(String str, String encode) {
try {
System.out.printf("字符: \\"%s\\" 的在编码方式%s下的十六进制值是%n", str, encode);
byte[] bs = str.getBytes(encode);

for (byte b : bs) {
int i = b&0xff;
System.out.print(Integer.toHexString(i) + "\\t");
}
System.out.println();
System.out.println();
} catch (UnsupportedEncodingException e) {
System.out.printf("UnsupportedEncodingException: %s编码方式无法解析字符%s\\n", encode, str);
}
}
}
\n

\"image-20210210233822081\"

\n

用FileInputStream 字节流正确读取中文

为了能够正确的读取中文内容

\n
    \n
  1. 必须了解文本是以哪种编码方式保存字符的

    \n
  2. \n
  3. 使用字节流读取文本

    \n
  4. \n
  5. 使用对应的编码方式去识别这些数字,得到正确的字符。
  6. \n
\n

如本例,一个文件中的内容是字符”中”,编码方式是GBK,那么读出来的数据一定是D6D0。

\n

再使用GBK编码方式识别D6D0,就能正确的得到字符

\n
\n

注: 在GBK的棋盘上找到的字后,JVM会自动找到在UNICODE这个棋盘上对应的数字,并且以UNICODE上的数字保存在内存中

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
File f = new File("d:\\\\project\\\\j2se\\\\src\\\\test.txt"); //该文件保存时的编码方式为GBK

try (FileInputStream fis = new FileInputStream(f);) {

byte[] all = new byte[(int) f.length()];

fis.read(all);\t//使用字节流读取文本

String str = new String(all,"GBK"); //解码,解码方式为GBK
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
}

}
}
\n

用FileReader 字符流正确读取中文

FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成为字符

\n

而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK

\n

FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替,像这样:

\n
1
new InputStreamReader(new FileInputStream(f), Charset.forName("UTF-8")); 
\n

缓存流

以介质是硬盘为例,字节流和字符流的弊端
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。

\n

为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。

\n

就好比吃饭,不用缓存就是每吃一口都到锅里去铲用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲。

\n

缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作,提高速度。

\n

使用缓存流读取数据

缓存字符输入流 BufferedReader 可以一次读取一行数据,但要注意,缓存流必须建立在一个存在的流的基础上

\n
\n

先准备好文件 d:/lol.txt,文件内容如下:

\n

garen kill teemo
teemo revive after 1 minutes
teemo try to garen, but killed again

\n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package stream;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {

File f = new File("d:/lol.txt");

try (
FileReader fr = new FileReader(f);\t\t// 创建文件字符流
BufferedReader br = new BufferedReader(fr); // 缓存流必须建立在一个存在的流的基础上
)
{
while (true) {
String line = br.readLine();// 一次读一行
if (line == null)
break;
System.out.println(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

使用缓存流写入数据

之前FileOutputStreamFileWriter要写入一串数据时,必须将数据转换为数组,一次只能写入一个字符。

\n

PrintWriter 缓存字符输出流, 可以一次写入一行数据;

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class TestStream {

public static void main(String[] args) {
// 向文件lol2.txt中写入三行语句
File f = new File("d:/lol2.txt");

try (
FileWriter fw = new FileWriter(f); // 创建文件字符流
PrintWriter pw = new PrintWriter(fw); // 缓存流必须建立在一个存在的流的基础上

) {
pw.println("garen kill teemo"); //写入一行数据
pw.println("teemo revive after 1 minutes");
pw.println("teemo try to garen, but killed again");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
\n

flush方法

有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush()方法

\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package stream;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class TestStream {
public static void main(String[] args) {
//向文件lol2.txt中写入三行语句
File f =new File("d:/lol2.txt");

try(
FileWriter fr = new FileWriter(f); //创建文件字符流
PrintWriter pw = new PrintWriter(fr); //缓存流必须建立在一个存在的流的基础上
) {
pw.println("garen kill teemo");
pw.flush(); //强制把缓存中的数据写入硬盘,无论缓存是否已满
pw.println("teemo revive after 1 minutes");
pw.flush();
pw.println("teemo try to garen, but killed again");
pw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
\n

数据流

    \n
  • DataInputStream 数据输入流
  • \n
  • DataOutputStream 数据输出流
  • \n
\n

直接读写字符串

使用数据流的writeUTF()readUTF() 可以进行数据的格式化顺序读写
如本例,通过DataOutputStream 向文件顺序写出【布尔值,整数和字符串】。 然后再通过DataInputStream 顺序读入这些数据。

\n
\n

注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException

\n

因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。

\n
\n

读取步骤:

\n
    \n
  1. 创建输入流 FileInputStream()
  2. \n
  3. 创建数据输入流 DataInputStream()
  4. \n
    • \n
    • 读取布尔值:boolean b= dis.readBoolean();
    • \n
    • 读取整数:int i = dis.readInt();
    • \n
    • 读取字符串:String str = dis.readUTF();
    • \n
    \n
  5. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
File f =new File("d:/lol.txt");
try (
FileInputStream fis = new FileInputStream(f);
DataInputStream dis =new DataInputStream(fis);
){
boolean b= dis.readBoolean();
int i = dis.readInt();
String str = dis.readUTF();

System.out.println("读取到布尔值:"+b);
System.out.println("读取到整数:"+i);
System.out.println("读取到字符串:"+str);

} catch (IOException e) {
e.printStackTrace();
}
\n

写入步骤:

\n
    \n
  1. 创建输出流:FileOutputStream()
  2. \n
  3. 创建数据输出流:DataOutputStream()
  4. \n
    • \n
    • 写入布尔值true:dos.writeBoolean(true)
    • \n
    • 写入整数:dos.writeInt(123)
    • \n
    • 写入字符串:dos.writeUTF("This is my string")
    • \n
    \n
  5. \n
\n
1
2
3
4
5
6
7
8
9
10
11
File f =new File("d:/lol.txt");
try (
FileOutputStream fos = new FileOutputStream(f);
DataOutputStream dos =new DataOutputStream(fos);
){
dos.writeBoolean(true);
dos.writeInt(300);
dos.writeUTF("123 this is gareen");
} catch (IOException e) {
e.printStackTrace();
}
\n

对象流

序列化一个对象

需要用到:

\n
    \n
  • 对象输入流:ObjectInputStream
  • \n
  • 对象输出流:ObjectOutputStream
  • \n
\n
\n

把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口

\n
\n
1
2
3
4
5
6
7
8
9
10
11
package charactor;

import java.io.Serializable;

public class Hero implements Serializable {
//表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
private static final long serialVersionUID = 1L;
public String name;
public float hp;

}
\n

步骤:

\n
    \n
  1. 创建一个Hero对象h,设置其名称为garen。
  2. \n
\n
1
2
3
Hero h = new Hero();
h.name = "garen";
h.hp = 616;
\n
    \n
  1. 把该对象序列化(即写入)到一个文件garen.lol
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
File f =new File("d:/garen.lol");

try(
//创建对象输出流
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos =new ObjectOutputStream(fos);

) {
oos.writeObject(h);

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
\n
    \n
  1. 然后再通过序列化把该文件转换为一个Hero对象
  2. \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
File f =new File("d:/garen.lol");

try(
//创建对象输入流
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois =new ObjectInputStream(fis);
){
Hero h2 = (Hero) ois.readObject();
\tSystem.out.println(h2.name);
\tSystem.out.println(h2.hp);
}catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
\n

System.in

    \n
  • System.out 是常用的在控制台输出数据的
  • \n
  • System.in 可以从控制台输入数据
  • \n
\n
1
2
3
4
5
6
7
8
9
10
11
12
13
try (InputStream is = System.in;) {
while (true) {
// 敲入a,然后敲回车可以看到
// 97 13 10
// 97是a的ASCII码
// 13 10分别对应回车换行
int i = is.read();
System.out.println(i);
}
} catch (IOException e) {
e.printStackTrace();
}

\n

Scanner读取字符串

使用System.in.read虽然可以读取数据,但是很不方便;
使用Scanner就可以逐行读取了

\n
1
import java.util.Scanner;  //使用前需要导入
\n
1
2
3
4
5
6
Scanner s = new Scanner(System.in);

while(true){
String line = s.nextLine();
System.out.println(line);
}
\n

Scanner从控制台读取整数

1
2
3
4
5
Scanner s = new Scanner(System.in);
int a = s.nextInt();
System.out.println("第一个整数:" + a);
int b = s.nextInt();
System.out.println("第二个整数:" + b);
\n"},{"title":"质数","description":"关于求质数的基本方法","abbrlink":"4c75c6b3","date":"2021-09-17T02:18:12.000Z","cover":null,"_content":"\n## 质数\n\n### 试除法判定质数\n\n\n### 试除法分解质因数\n\n\n### 朴素筛选法筛选质数\n\n\n### 线性筛选法筛选质数","source":"_posts/算法/质数.md","raw":"---\ntitle: 质数\ndescription: 关于求质数的基本方法\ntags:\n - 质数\ncategories:\n - 算法\n - 数学知识\nabbrlink: 4c75c6b3\ndate: 2021-09-17 10:18:12\ncover:\n---\n\n## 质数\n\n### 试除法判定质数\n\n\n### 试除法分解质因数\n\n\n### 朴素筛选法筛选质数\n\n\n### 线性筛选法筛选质数","slug":"算法/质数","published":1,"updated":"2021-09-17T02:27:23.416Z","_id":"cktnqr7b6000084ve2u2250ie","comments":1,"layout":"post","photos":[],"link":""}],"PostAsset":[],"PostCategory":[{"post_id":"cktk8o6og000vakve4p1e67aw","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6oj0014akvec2hqhwyu"},{"post_id":"cktk8o6og000vakve4p1e67aw","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6ol0019akve3a9nedjg"},{"post_id":"cktk8o6o40009akvehm657ia2","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6om001cakve6jks286m"},{"post_id":"cktk8o6o40009akvehm657ia2","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6oo001hakvebl2t8950"},{"post_id":"cktk8o6nw0001akve96rfdnwk","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6oq001kakve7f9nbg4e"},{"post_id":"cktk8o6nw0001akve96rfdnwk","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6ou001oakvef7m1avlf"},{"post_id":"cktk8o6o6000bakve2lmd5swc","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6ov001rakvea22maeyl"},{"post_id":"cktk8o6o6000bakve2lmd5swc","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6ow001vakvea67i0c4i"},{"post_id":"cktk8o6o9000gakve5fnphajo","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6ox001yakvec2amfe1j"},{"post_id":"cktk8o6o9000gakve5fnphajo","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6p40022akvefzqbco0t"},{"post_id":"cktk8o6nz0003akve5a7o3s93","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6p60025akve1fx4bsof"},{"post_id":"cktk8o6nz0003akve5a7o3s93","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6p8002aakve65yg6zu2"},{"post_id":"cktk8o6ob000jakve6uur2o8z","category_id":"cktk8o6ov001sakve83h63fm7","_id":"cktk8o6p9002dakve82hs3hgf"},{"post_id":"cktk8o6od000oakveeb9m24v2","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6pa002hakvecn1acudr"},{"post_id":"cktk8o6od000oakveeb9m24v2","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6pc002kakvefxwr9kpf"},{"post_id":"cktk8o6o30007akve3mey9pm8","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6pd002oakvedtxharhl"},{"post_id":"cktk8o6o30007akve3mey9pm8","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6pe002rakve52fe3teo"},{"post_id":"cktk8o6oe000rakve4wdsbjyw","category_id":"cktk8o6ov001sakve83h63fm7","_id":"cktk8o6pf002vakvef3qt7h1a"},{"post_id":"cktk8o6oi0011akve2ujw438x","category_id":"cktk8o6ov001sakve83h63fm7","_id":"cktk8o6pi0034akve343p7hbs"},{"post_id":"cktk8o6pc002makvec2g3cfzf","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qk005iakve0ndk6bl8"},{"post_id":"cktk8o6pc002makvec2g3cfzf","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6ql005lakve3z9ndia8"},{"post_id":"cktk8o6og000xakve4mebdy6p","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qm005oakve5hkw9h5n"},{"post_id":"cktk8o6og000xakve4mebdy6p","category_id":"cktk8o6qh005bakveacjzeuio","_id":"cktk8o6qn005sakve5vwg126m"},{"post_id":"cktk8o6pd002qakve0ic15g6q","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qp005xakve75rna0l3"},{"post_id":"cktk8o6pd002qakve0ic15g6q","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6qq0060akve288y7433"},{"post_id":"cktk8o6pe002takvedtbz9vw9","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qr0064akvecp3540c5"},{"post_id":"cktk8o6pe002takvedtbz9vw9","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6qs0067akvefp57gzbj"},{"post_id":"cktk8o6pf002xakve102r3c73","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qt006bakvedmok4bmg"},{"post_id":"cktk8o6pf002xakve102r3c73","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6qu006eakve3z5213g7"},{"post_id":"cktk8o6pg002zakve9wvvbwrm","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qv006iakved3f2hbpd"},{"post_id":"cktk8o6pg002zakve9wvvbwrm","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6qw006lakvees006wdv"},{"post_id":"cktk8o6oj0013akve45f3gq5i","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qy006pakvea6l7fwa2"},{"post_id":"cktk8o6oj0013akve45f3gq5i","category_id":"cktk8o6qs0068akve66aj2w3e","_id":"cktk8o6qy006sakvedowuh8yj"},{"post_id":"cktk8o6pi0032akve2d4z141s","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6qz006wakveg55ndm47"},{"post_id":"cktk8o6pi0032akve2d4z141s","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6r1006yakve3ryy1253"},{"post_id":"cktk8o6pj0035akvehrre9dbw","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r20072akve8hwocydq"},{"post_id":"cktk8o6pj0035akvehrre9dbw","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6r30075akvecxsaby1t"},{"post_id":"cktk8o6ok0018akve39f5g6yx","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r4007aakveaoqffsfs"},{"post_id":"cktk8o6ok0018akve39f5g6yx","category_id":"cktk8o6qs0068akve66aj2w3e","_id":"cktk8o6r5007dakvefju87c73"},{"post_id":"cktk8o6pk0038akved9jw5nm3","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r6007hakve2r7g0cb9"},{"post_id":"cktk8o6pk0038akved9jw5nm3","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6r7007kakve6md255cy"},{"post_id":"cktk8o6pl003bakve3evtamml","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6r8007oakve8nt917sa"},{"post_id":"cktk8o6pl003bakve3evtamml","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6r9007rakve1gtz1qyh"},{"post_id":"cktk8o6ol001bakve1lqxghp8","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rb007vakveeup23brs"},{"post_id":"cktk8o6ol001bakve1lqxghp8","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6rc007xakveabd7dwj5"},{"post_id":"cktk8o6pm003eakve82ox37f6","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rd0081akvebeht2p62"},{"post_id":"cktk8o6pm003eakve82ox37f6","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6re0084akvehojk4gzq"},{"post_id":"cktk8o6pn003hakvebel7gurk","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rf0089akvecsfx061p"},{"post_id":"cktk8o6pn003hakvebel7gurk","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6rg008bakve8tmz7tpr"},{"post_id":"cktk8o6oo001gakve5kd5ad6i","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rh008fakveeead0b0t"},{"post_id":"cktk8o6oo001gakve5kd5ad6i","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6rh008iakve39tz9l2h"},{"post_id":"cktk8o6po003kakveajnw0fjv","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rj008nakve41pf150g"},{"post_id":"cktk8o6po003kakveajnw0fjv","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6rk008qakve4c1858xj"},{"post_id":"cktk8o6pp003nakve3uzjgiks","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rl008takve5l5rgg4w"},{"post_id":"cktk8o6pp003nakve3uzjgiks","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6rl008vakve50hgd766"},{"post_id":"cktk8o6oq001jakvebouthuqu","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rm008yakve13rd38s6"},{"post_id":"cktk8o6oq001jakvebouthuqu","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6rn0090akvegoka2g7k"},{"post_id":"cktk8o6pq003pakveexb0ak5a","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ro0093akve37gm5y6d"},{"post_id":"cktk8o6pq003pakveexb0ak5a","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6ro0095akvec3593av6"},{"post_id":"cktk8o6pr003sakve2exe8imp","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rp0098akvegq5f80fe"},{"post_id":"cktk8o6pr003sakve2exe8imp","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6rp009aakvecf3ubpum"},{"post_id":"cktk8o6or001nakved1j01zfv","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6rp009dakvea5u370pm"},{"post_id":"cktk8o6or001nakved1j01zfv","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6rq009fakvec4c2ci8i"},{"post_id":"cktk8o6ps003uakve66vo01gc","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rq009iakve27tbbabk"},{"post_id":"cktk8o6ps003uakve66vo01gc","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6rr009kakve1s5thkhh"},{"post_id":"cktk8o6pt003xakve53qs5vxp","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rr009nakve3yt32ssg"},{"post_id":"cktk8o6pt003xakve53qs5vxp","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6rs009pakvedy8k8cy2"},{"post_id":"cktk8o6ou001qakve1mo0aal1","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ru009sakve4soj6eqc"},{"post_id":"cktk8o6ou001qakve1mo0aal1","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6rv009uakveazdn6skv"},{"post_id":"cktk8o6pu003zakve3wlwdsem","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6rx009xakve7wdi4ywd"},{"post_id":"cktk8o6pu003zakve3wlwdsem","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6ry009zakve2o6igl5f"},{"post_id":"cktk8o6pv0042akve76zr0o90","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6ry00a2akve4wblgq9f"},{"post_id":"cktk8o6pv0042akve76zr0o90","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6rz00a4akveatgobtqf"},{"post_id":"cktk8o6ow001uakve6hqegt4b","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6s000a6akvedf0c476i"},{"post_id":"cktk8o6ow001uakve6hqegt4b","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6s000a9akvegvax65sy"},{"post_id":"cktk8o6pw0044akveh5zqdjte","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s100abakveclkgefvx"},{"post_id":"cktk8o6pw0044akveh5zqdjte","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6s100adakve1uyedra5"},{"post_id":"cktk8o6px0047akve8seydg2u","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s200agakve0dwk82ox"},{"post_id":"cktk8o6px0047akve8seydg2u","category_id":"cktk8o6rz00a5akvehafeesla","_id":"cktk8o6s200aiakved1yw255w"},{"post_id":"cktk8o6ox001xakvegmtv0lsp","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6s300alakveb1sa8ry9"},{"post_id":"cktk8o6ox001xakvegmtv0lsp","category_id":"cktk8o6r5007eakve6g5i7k0f","_id":"cktk8o6s300aoakvehbzcggp2"},{"post_id":"cktk8o6py0049akve15r5eo74","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s400aqakved8ayh2cx"},{"post_id":"cktk8o6py0049akve15r5eo74","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6s400atakveeh754wak"},{"post_id":"cktk8o6py004cakve0g6zci7t","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s500avakvecb8x5os0"},{"post_id":"cktk8o6py004cakve0g6zci7t","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6s500ayakve6htn052r"},{"post_id":"cktk8o6oy0020akvegdxh7rqn","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6s500b0akvef2ny4vth"},{"post_id":"cktk8o6oy0020akvegdxh7rqn","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6s700b3akve545w7eu1"},{"post_id":"cktk8o6q0004eakve6dzd49z7","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s800b5akve0px41c1n"},{"post_id":"cktk8o6q0004eakve6dzd49z7","category_id":"cktk8o6rl008wakveg62hemot","_id":"cktk8o6s800b8akvegfnz5ffg"},{"post_id":"cktk8o6q1004hakve7ci71f3m","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6s900baakve0slnezxu"},{"post_id":"cktk8o6q1004hakve7ci71f3m","category_id":"cktk8o6rz00a5akvehafeesla","_id":"cktk8o6s900bdakvehmqk7ryb"},{"post_id":"cktk8o6p60024akvegkx6f24q","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sa00bfakvede71avgc"},{"post_id":"cktk8o6p60024akvegkx6f24q","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6sa00biakve44sz9l3k"},{"post_id":"cktk8o6q2004jakvedojmd1vx","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6sa00bkakve3r9d6xsx"},{"post_id":"cktk8o6q2004jakvedojmd1vx","category_id":"cktk8o6rz00a5akvehafeesla","_id":"cktk8o6sb00bnakvefbfxfuzl"},{"post_id":"cktk8o6q4004makve8gio9l5r","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sb00bqakve2o4h2u0i"},{"post_id":"cktk8o6q4004makve8gio9l5r","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6sc00bsakvec7oza7gp"},{"post_id":"cktk8o6p70028akve23cieqja","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sc00bvakve1l9uglyx"},{"post_id":"cktk8o6p70028akve23cieqja","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6sd00bxakvef85sbodq"},{"post_id":"cktk8o6q5004oakve8g0k1g9x","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6se00c0akvefw0r9pq1"},{"post_id":"cktk8o6q5004oakve8g0k1g9x","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6se00c2akvea2ze4kry"},{"post_id":"cktk8o6q6004rakve7m1j1plt","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sf00c5akve4bsm0ziq"},{"post_id":"cktk8o6q6004rakve7m1j1plt","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6sg00c7akve206h80uw"},{"post_id":"cktk8o6p8002cakvedimceatv","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sh00caakve0leo7zey"},{"post_id":"cktk8o6p8002cakvedimceatv","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6sh00ccakve188ba77o"},{"post_id":"cktk8o6q7004takvecc6ph7mb","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6si00cfakvefab977bu"},{"post_id":"cktk8o6q7004takvecc6ph7mb","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6si00chakve0fuv2dre"},{"post_id":"cktk8o6q9004wakve8yyz5ix5","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sj00ckakve1g9yarhv"},{"post_id":"cktk8o6q9004wakve8yyz5ix5","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6sj00cmakvedm3t4jiu"},{"post_id":"cktk8o6p9002fakve7ps4flkp","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sk00cpakve64ncfnjw"},{"post_id":"cktk8o6p9002fakve7ps4flkp","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6sk00crakve10p3d2yz"},{"post_id":"cktk8o6qb004yakve5qk6a50r","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sl00cuakve1crm94d5"},{"post_id":"cktk8o6qb004yakve5qk6a50r","category_id":"cktk8o6s900beakvegzr10ttv","_id":"cktk8o6sl00cwakve05qffyqm"},{"post_id":"cktk8o6qc0051akve1nql6qt7","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sm00czakvehqwsedkl"},{"post_id":"cktk8o6qc0051akve1nql6qt7","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6sm00d1akve7zyi46tw"},{"post_id":"cktk8o6pb002jakvefzhpc3tk","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6so00d4akvehnbbc4vq"},{"post_id":"cktk8o6pb002jakvefzhpc3tk","category_id":"cktk8o6qe0055akvegob69vva","_id":"cktk8o6so00d6akve3pv156de"},{"post_id":"cktk8o6qd0053akve87tvdadq","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sp00d9akve5pau1now"},{"post_id":"cktk8o6qd0053akve87tvdadq","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6sp00dbakve0gd182ew"},{"post_id":"cktk8o6qe0056akvea9ks9ha1","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sq00deakvead4chvyt"},{"post_id":"cktk8o6qe0056akvea9ks9ha1","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6sq00dgakvehlqqbnir"},{"post_id":"cktk8o6qf0058akve50qfda62","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sq00djakvebfpncx6n"},{"post_id":"cktk8o6qf0058akve50qfda62","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6sr00dlakve9i01fx4u"},{"post_id":"cktk8o6qg005aakveh3vd1y50","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sr00dnakveatrt5zjy"},{"post_id":"cktk8o6qg005aakveh3vd1y50","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6ss00dqakve9x6nh8id"},{"post_id":"cktk8o6qj005dakve37dy5lne","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ss00dsakvecun1ebgg"},{"post_id":"cktk8o6qj005dakve37dy5lne","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6ss00dvakvefm0w4z15"},{"post_id":"cktk8o6qj005fakvegpvj86dz","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6st00dxakve9sve4p0j"},{"post_id":"cktk8o6qj005fakvegpvj86dz","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6st00e0akvefdv62me0"},{"post_id":"cktk8o6ql005jakve8xo8cwcv","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6su00e2akve5byn9ejm"},{"post_id":"cktk8o6ql005jakve8xo8cwcv","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6sx00e5akved6gu0o54"},{"post_id":"cktk8o6ql005makve7a1dc654","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6sy00e7akvecj5d430r"},{"post_id":"cktk8o6ql005makve7a1dc654","category_id":"cktk8o6sj00cnakvedfi1cpk5","_id":"cktk8o6sz00eaakvecy9nfta8"},{"post_id":"cktk8o6qn005qakve26rcd19s","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t400ecakve6v23dpmg"},{"post_id":"cktk8o6qn005qakve26rcd19s","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t400eeakvefw6i8me2"},{"post_id":"cktk8o6qn005takveawfj22pb","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t500ehakvefpgo453g"},{"post_id":"cktk8o6qn005takveawfj22pb","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t500ejakve196reb6y"},{"post_id":"cktk8o6qp005wakvedimp04vg","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t600emakve4bn4aa02"},{"post_id":"cktk8o6qp005wakvedimp04vg","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t600eoakve06ijbfyn"},{"post_id":"cktk8o6qq005zakve2q775ly3","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t700erakvefcgw2gl7"},{"post_id":"cktk8o6qq005zakve2q775ly3","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t700etakve7x6u5y46"},{"post_id":"cktk8o6qr0063akvedrb617kz","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t800ewakve4zw7fd9w"},{"post_id":"cktk8o6qr0063akvedrb617kz","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t800exakvee5c977vg"},{"post_id":"cktk8o6qs0066akve3ppb6e1q","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t800f0akve33ocdtke"},{"post_id":"cktk8o6qs0066akve3ppb6e1q","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t800f1akved74ka716"},{"post_id":"cktk8o6qt006aakvealgt52ey","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t900f3akve3f7c4tfw"},{"post_id":"cktk8o6qt006aakvealgt52ey","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t900f4akvehcpj04vk"},{"post_id":"cktk8o6qu006dakve8q0q3khe","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6t900f6akvec70ubym2"},{"post_id":"cktk8o6qu006dakve8q0q3khe","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6t900f7akve4ed5a69k"},{"post_id":"cktk8o6qv006hakve2qm4crpn","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ta00f9akvebawxa825"},{"post_id":"cktk8o6qv006hakve2qm4crpn","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6ta00faakvefml23gpd"},{"post_id":"cktk8o6qw006kakvec7h0d155","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ta00fcakve7dqwh5is"},{"post_id":"cktk8o6qw006kakvec7h0d155","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6ta00fdakve7r385sl4"},{"post_id":"cktk8o6qx006oakveh6kj9q64","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6ta00ffakve6h0ndec6"},{"post_id":"cktk8o6qx006oakveh6kj9q64","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tb00fgakvegqcjh9ur"},{"post_id":"cktk8o6qy006rakve1z853ljh","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tb00fiakveakcceace"},{"post_id":"cktk8o6qy006rakve1z853ljh","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tb00fjakve32z7goif"},{"post_id":"cktk8o6qz006uakve9nqxfhzr","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tb00flakve5n9th5kp"},{"post_id":"cktk8o6qz006uakve9nqxfhzr","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tb00fmakved1qf5tq4"},{"post_id":"cktk8o6r0006xakve9680bvvn","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tc00foakve4ujj0bni"},{"post_id":"cktk8o6r0006xakve9680bvvn","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tc00fpakve24jo659q"},{"post_id":"cktk8o6r10070akvedu2zcjwq","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tc00frakvegtsa3uqm"},{"post_id":"cktk8o6r10070akvedu2zcjwq","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tc00fsakve75xialww"},{"post_id":"cktk8o6r20074akve5rp09eoj","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6td00fuakveaqxu6ze2"},{"post_id":"cktk8o6r20074akve5rp09eoj","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6td00fvakve78e72sk6"},{"post_id":"cktk8o6r30078akvehflj72w7","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6td00fxakve4tvabr2v"},{"post_id":"cktk8o6r30078akvehflj72w7","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6td00fyakvegw541tad"},{"post_id":"cktk8o6r5007cakve2yiob0j3","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6td00g0akvefnlue3z8"},{"post_id":"cktk8o6r5007cakve2yiob0j3","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6te00g1akvegp4l3r08"},{"post_id":"cktk8o6r5007fakve7bba4t62","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6te00g3akvehksf7yy2"},{"post_id":"cktk8o6r5007fakve7bba4t62","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6te00g4akve4sdf5wkn"},{"post_id":"cktk8o6r6007jakvegi6bdbyo","category_id":"cktk8o6td00fzakve55x24m4x","_id":"cktk8o6te00g6akvee17p0sm2"},{"post_id":"cktk8o6r7007lakve2vjjaimr","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tf00g8akvefh3l9yxw"},{"post_id":"cktk8o6r7007lakve2vjjaimr","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tf00g9akvee7ah76yg"},{"post_id":"cktk8o6r8007qakvebtub8ep8","category_id":"cktk8o6td00fzakve55x24m4x","_id":"cktk8o6tf00gbakve1cq302x0"},{"post_id":"cktk8o6r9007sakvea9jb34oj","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6tf00gdakve4zmtbi2u"},{"post_id":"cktk8o6r9007sakvea9jb34oj","category_id":"cktk8o6te00g7akvedfi64j0n","_id":"cktk8o6tg00geakve1cgz3vh4"},{"post_id":"cktk8o6rb007wakve7c6t6uot","category_id":"cktk8o6td00fzakve55x24m4x","_id":"cktk8o6tg00ggakve73vz7ott"},{"post_id":"cktk8o6rc007yakve8853hjrp","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6tg00giakved18646cx"},{"post_id":"cktk8o6rc007yakve8853hjrp","category_id":"cktk8o6te00g7akvedfi64j0n","_id":"cktk8o6th00gjakve5n34ayg7"},{"post_id":"cktk8o6rd0083akve82iv1jsm","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6th00glakve8ny79chw"},{"post_id":"cktk8o6rd0083akve82iv1jsm","category_id":"cktk8o6te00g7akvedfi64j0n","_id":"cktk8o6th00gmakveexqo8pa5"},{"post_id":"cktk8o6re0086akve1a6d4ffa","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6th00gnakveb44wezxq"},{"post_id":"cktk8o6re0086akve1a6d4ffa","category_id":"cktk8o6te00g7akvedfi64j0n","_id":"cktk8o6th00gpakve47l20r49"},{"post_id":"cktk8o6rf008aakve5eeddxx1","category_id":"cktk8o6th00gkakve2e1eb0kc","_id":"cktk8o6th00gqakvec87uhpbu"},{"post_id":"cktk8o6rh008hakveh2yhheff","category_id":"cktk8o6th00grakve7iws2msc","_id":"cktk8o6ti00guakveeza12uof"},{"post_id":"cktk8o6ri008kakve8zmh4jwv","category_id":"cktk8o6th00grakve7iws2msc","_id":"cktk8o6ti00gvakvehtlo554s"},{"post_id":"cktk8o6rj008oakvef3va6zbp","category_id":"cktk8o6ti00gtakve51ks4u05","_id":"cktk8o6tj00gxakve02ad9rcf"},{"post_id":"cktk8o6rg008cakve0mvn1u4x","category_id":"cktk8o6th00goakve1pju20c4","_id":"cktk8o6tj00gyakve1wwehem0"},{"post_id":"cktk8o6rg008cakve0mvn1u4x","category_id":"cktk8o6ti00gwakve0x2ufj97","_id":"cktk8o6tj00gzakve9vjgfnsd"},{"post_id":"cktk8o6tt00h0akvehg5w1fi3","category_id":"cktk8o6o10004akve0nho022p","_id":"cktk8o6tw00h6akved2p631x6"},{"post_id":"cktk8o6tt00h0akvehg5w1fi3","category_id":"cktk8o6oe000sakvef4qjae8o","_id":"cktk8o6tw00h9akve9hz2d46f"},{"post_id":"cktk8o6tu00h1akve7d054dj8","category_id":"cktk8o6ov001sakve83h63fm7","_id":"cktk8o6ty00hcakvecs0narij"},{"post_id":"cktk8o6tu00h3akve8scabvi5","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tz00heakveduta8x9y"},{"post_id":"cktk8o6tu00h3akve8scabvi5","category_id":"cktk8o6qh005bakveacjzeuio","_id":"cktk8o6tz00hgakvegnye9o21"},{"post_id":"cktk8o6tv00h5akvefji7akri","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tz00hhakve3pzjfoak"},{"post_id":"cktk8o6tv00h5akvefji7akri","category_id":"cktk8o6r30076akve2siqh8cu","_id":"cktk8o6tz00hiakvehqy29hk7"},{"post_id":"cktk8o6tw00h8akveg72k4lri","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tz00hjakve6drfemmf"},{"post_id":"cktk8o6tw00h8akveg72k4lri","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6tz00hkakvebh5jgt20"},{"post_id":"cktk8o6tx00hbakve9wq17mn4","category_id":"cktk8o6pc002lakve2iyjem8n","_id":"cktk8o6tz00hlakveeii8c7k3"},{"post_id":"cktk8o6tx00hbakve9wq17mn4","category_id":"cktk8o6sq00dhakve6e73glbu","_id":"cktk8o6u000hmakve3o798w0s"},{"post_id":"cktnqr7b6000084ve2u2250ie","category_id":"cktnqr7by000184vecgng4l09","_id":"cktnqr7fg000584ve4ueibxhd"},{"post_id":"cktnqr7b6000084ve2u2250ie","category_id":"cktnqr7fd000484ve3lueg94u","_id":"cktnqr7fh000684ve3837bna0"}],"PostTag":[{"post_id":"cktk8o6o40009akvehm657ia2","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6o9000eakve99xw03t8"},{"post_id":"cktk8o6nw0001akve96rfdnwk","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6oa000hakvea9x224t8"},{"post_id":"cktk8o6o6000bakve2lmd5swc","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6oc000makvebz276qc1"},{"post_id":"cktk8o6o9000gakve5fnphajo","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6od000pakveh7lbd9ii"},{"post_id":"cktk8o6nz0003akve5a7o3s93","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6of000uakve4u9efz7l"},{"post_id":"cktk8o6od000oakveeb9m24v2","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6og000wakveaa9m4yhh"},{"post_id":"cktk8o6o30007akve3mey9pm8","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6oi0010akve1srnhcfv"},{"post_id":"cktk8o6og000vakve4p1e67aw","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6oj0012akve9jjncu8h"},{"post_id":"cktk8o6ob000jakve6uur2o8z","tag_id":"cktk8o6of000takvegfo3ddk2","_id":"cktk8o6ok0017akve1g8z5hin"},{"post_id":"cktk8o6oi0011akve2ujw438x","tag_id":"cktk8o6of000takvegfo3ddk2","_id":"cktk8o6ol001aakvebqgg0lpv"},{"post_id":"cktk8o6oe000rakve4wdsbjyw","tag_id":"cktk8o6of000takvegfo3ddk2","_id":"cktk8o6on001fakveg9jxfs9l"},{"post_id":"cktk8o6og000xakve4mebdy6p","tag_id":"cktk8o6ok0016akve4lvz1g74","_id":"cktk8o6oq001iakvecj0n78f9"},{"post_id":"cktk8o6oj0013akve45f3gq5i","tag_id":"cktk8o6om001eakvedgux10w5","_id":"cktk8o6ou001pakvecf8g1zee"},{"post_id":"cktk8o6ok0018akve39f5g6yx","tag_id":"cktk8o6om001eakvedgux10w5","_id":"cktk8o6ox001wakveb8dx0lqh"},{"post_id":"cktk8o6ox001xakvegmtv0lsp","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6p40023akvef1zw9vl8"},{"post_id":"cktk8o6ol001bakve1lqxghp8","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6p70027akve087n3kny"},{"post_id":"cktk8o6oo001gakve5kd5ad6i","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6p8002bakveej0jgqvd"},{"post_id":"cktk8o6oq001jakvebouthuqu","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6pa002iakveac4083j8"},{"post_id":"cktk8o6or001nakved1j01zfv","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6pd002pakve1emm0k2o"},{"post_id":"cktk8o6ou001qakve1mo0aal1","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6pf002wakve91ln64n6"},{"post_id":"cktk8o6ow001uakve6hqegt4b","tag_id":"cktk8o6ow001takveefgsbxmx","_id":"cktk8o6ph0031akvedjml1ivm"},{"post_id":"cktk8o6pi0032akve2d4z141s","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pk0037akvedppuds1l"},{"post_id":"cktk8o6oy0020akvegdxh7rqn","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pk0039akve8gwz2hff"},{"post_id":"cktk8o6pj0035akvehrre9dbw","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pm003dakveef8l6vmf"},{"post_id":"cktk8o6pk0038akved9jw5nm3","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pn003fakvedgofapqd"},{"post_id":"cktk8o6p60024akvegkx6f24q","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6po003jakvebiypdp1z"},{"post_id":"cktk8o6p70028akve23cieqja","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pp003lakve329g61b7"},{"post_id":"cktk8o6p8002cakvedimceatv","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pq003qakve1gc28zby"},{"post_id":"cktk8o6p9002fakve7ps4flkp","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6ps003vakvefapxfrjf"},{"post_id":"cktk8o6pb002jakvefzhpc3tk","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pu0040akve29hpfqs1"},{"post_id":"cktk8o6pc002makvec2g3cfzf","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6pw0045akvecucu5nz1"},{"post_id":"cktk8o6pd002qakve0ic15g6q","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6py004aakvefaifd5ng"},{"post_id":"cktk8o6pe002takvedtbz9vw9","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6q1004fakve3s3p41zm"},{"post_id":"cktk8o6pf002xakve102r3c73","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6q3004kakve7ubbfiuh"},{"post_id":"cktk8o6pg002zakve9wvvbwrm","tag_id":"cktk8o6ph0030akve7x5udh45","_id":"cktk8o6q5004pakveh0fqac69"},{"post_id":"cktk8o6pl003bakve3evtamml","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6q9004uakvec8aifdz3"},{"post_id":"cktk8o6pm003eakve82ox37f6","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6qc004zakved5legib1"},{"post_id":"cktk8o6pn003hakvebel7gurk","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6qe0054akve8yn53scq"},{"post_id":"cktk8o6po003kakveajnw0fjv","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6qg0059akvefjzg81rv"},{"post_id":"cktk8o6pp003nakve3uzjgiks","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6qj005eakvecpt56v8v"},{"post_id":"cktk8o6pq003pakveexb0ak5a","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6ql005kakveceycaoh2"},{"post_id":"cktk8o6pr003sakve2exe8imp","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qn005rakve0j7v0rbn"},{"post_id":"cktk8o6ps003uakve66vo01gc","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qp005yakve3bnpd8am"},{"post_id":"cktk8o6pt003xakve53qs5vxp","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qr0065akvebg8p8jkw"},{"post_id":"cktk8o6pu003zakve3wlwdsem","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qu006cakve2r3e6hio"},{"post_id":"cktk8o6pv0042akve76zr0o90","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qw006jakve63ipa2y6"},{"post_id":"cktk8o6pw0044akveh5zqdjte","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6qy006qakveh4l0fc9w"},{"post_id":"cktk8o6px0047akve8seydg2u","tag_id":"cktk8o6qx006nakvefl9gfr5d","_id":"cktk8o6r20073akvecg2sdqkl"},{"post_id":"cktk8o6px0047akve8seydg2u","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6r30077akve6izva8aw"},{"post_id":"cktk8o6py0049akve15r5eo74","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6r5007bakve6bi5b95j"},{"post_id":"cktk8o6py004cakve0g6zci7t","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6r6007iakveabawgygy"},{"post_id":"cktk8o6q0004eakve6dzd49z7","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6r8007pakve0hricnbi"},{"post_id":"cktk8o6q1004hakve7ci71f3m","tag_id":"cktk8o6qx006nakvefl9gfr5d","_id":"cktk8o6rd0082akveebnh4zfb"},{"post_id":"cktk8o6q1004hakve7ci71f3m","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6re0085akvegku0657k"},{"post_id":"cktk8o6q2004jakvedojmd1vx","tag_id":"cktk8o6qx006nakvefl9gfr5d","_id":"cktk8o6rh008gakve0c5u6vme"},{"post_id":"cktk8o6q2004jakvedojmd1vx","tag_id":"cktk8o6qk005hakve2n3l2y1j","_id":"cktk8o6ri008jakve21jx5ws3"},{"post_id":"cktk8o6q4004makve8gio9l5r","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6rk008pakve14ohgsxh"},{"post_id":"cktk8o6q5004oakve8g0k1g9x","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6rl008uakve60pnc5xl"},{"post_id":"cktk8o6q6004rakve7m1j1plt","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6rm008zakve8zfec08u"},{"post_id":"cktk8o6q7004takvecc6ph7mb","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6ro0094akve58ztg33g"},{"post_id":"cktk8o6q9004wakve8yyz5ix5","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6rp0099akve8x889ecg"},{"post_id":"cktk8o6qb004yakve5qk6a50r","tag_id":"cktk8o6rg008eakve4ce71gs4","_id":"cktk8o6rp009eakvegwlkgex4"},{"post_id":"cktk8o6qc0051akve1nql6qt7","tag_id":"cktk8o6rp009cakvecleqe5lf","_id":"cktk8o6rq009jakve54gh4g6m"},{"post_id":"cktk8o6qd0053akve87tvdadq","tag_id":"cktk8o6rp009cakvecleqe5lf","_id":"cktk8o6rr009oakvefdz6362h"},{"post_id":"cktk8o6qe0056akvea9ks9ha1","tag_id":"cktk8o6rp009cakvecleqe5lf","_id":"cktk8o6rv009takveh4eo6q7z"},{"post_id":"cktk8o6qf0058akve50qfda62","tag_id":"cktk8o6ru009rakve2ntgf97s","_id":"cktk8o6ry009yakvehat0a2nv"},{"post_id":"cktk8o6qg005aakveh3vd1y50","tag_id":"cktk8o6rp009cakvecleqe5lf","_id":"cktk8o6rz00a3akve8za18n5w"},{"post_id":"cktk8o6qj005dakve37dy5lne","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s000a8akve2n5j4l3s"},{"post_id":"cktk8o6qj005fakvegpvj86dz","tag_id":"cktk8o6s000a7akve1f33c9rq","_id":"cktk8o6s100aeakve87e8emc9"},{"post_id":"cktk8o6ql005jakve8xo8cwcv","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s200ajakvec4c44dz8"},{"post_id":"cktk8o6ql005makve7a1dc654","tag_id":"cktk8o6ru009rakve2ntgf97s","_id":"cktk8o6s300anakvee7ny0dz1"},{"post_id":"cktk8o6qn005qakve26rcd19s","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s400asakve19n9ck5b"},{"post_id":"cktk8o6qn005takveawfj22pb","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s500axakve166lfsxn"},{"post_id":"cktk8o6qp005wakvedimp04vg","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s700b2akveadv5bgnz"},{"post_id":"cktk8o6qq005zakve2q775ly3","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s800b7akve18uee020"},{"post_id":"cktk8o6qr0063akvedrb617kz","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6s900bcakve1a06hfrq"},{"post_id":"cktk8o6qs0066akve3ppb6e1q","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sa00bhakveasvl2yw4"},{"post_id":"cktk8o6qt006aakvealgt52ey","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sb00bmakve0tdi89f4"},{"post_id":"cktk8o6qu006dakve8q0q3khe","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sc00brakvecbru8jgt"},{"post_id":"cktk8o6qv006hakve2qm4crpn","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sd00bwakve73vw8w1m"},{"post_id":"cktk8o6qw006kakvec7h0d155","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6se00c1akve7o6o2tdi"},{"post_id":"cktk8o6qx006oakveh6kj9q64","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sg00c6akvehk2i67pg"},{"post_id":"cktk8o6qy006rakve1z853ljh","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sh00cbakve07uyfw5t"},{"post_id":"cktk8o6qz006uakve9nqxfhzr","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6si00cgakve03bb0pf8"},{"post_id":"cktk8o6r0006xakve9680bvvn","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sj00clakve62er0udh"},{"post_id":"cktk8o6r10070akvedu2zcjwq","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sk00cqakve7z2wacsk"},{"post_id":"cktk8o6r20074akve5rp09eoj","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sl00cvakvecklogdi3"},{"post_id":"cktk8o6r30078akvehflj72w7","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sm00d0akvedeuv4pop"},{"post_id":"cktk8o6r5007cakve2yiob0j3","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6so00d5akvegyx8fk5p"},{"post_id":"cktk8o6r5007fakve7bba4t62","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sp00daakvedd9x8x0f"},{"post_id":"cktk8o6r7007lakve2vjjaimr","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6sq00dfakve4oz690bc"},{"post_id":"cktk8o6r8007qakvebtub8ep8","tag_id":"cktk8o6sq00ddakve9lizf5ee","_id":"cktk8o6sr00dkakve5j4i5unw"},{"post_id":"cktk8o6r9007sakvea9jb34oj","tag_id":"cktk8o6sq00diakve7a9v2s38","_id":"cktk8o6sr00dpakve61vf3t37"},{"post_id":"cktk8o6rb007wakve7c6t6uot","tag_id":"cktk8o6sq00ddakve9lizf5ee","_id":"cktk8o6ss00duakve59ev8uss"},{"post_id":"cktk8o6rc007yakve8853hjrp","tag_id":"cktk8o6ss00dtakvedmobezfr","_id":"cktk8o6st00dzakve4dbn9di2"},{"post_id":"cktk8o6rd0083akve82iv1jsm","tag_id":"cktk8o6ss00dtakvedmobezfr","_id":"cktk8o6su00e4akveazragvqw"},{"post_id":"cktk8o6re0086akve1a6d4ffa","tag_id":"cktk8o6ss00dtakvedmobezfr","_id":"cktk8o6sz00e9akve3hzsgsos"},{"post_id":"cktk8o6rf008aakve5eeddxx1","tag_id":"cktk8o6sy00e8akveh7cobd7s","_id":"cktk8o6t400efakve2f24927f"},{"post_id":"cktk8o6rg008cakve0mvn1u4x","tag_id":"cktk8o6t400edakveb0x0b1yt","_id":"cktk8o6t500elakvebsk5fpor"},{"post_id":"cktk8o6rh008hakveh2yhheff","tag_id":"cktk8o6t500eiakveb5k3115m","_id":"cktk8o6t700eqakve6f1x5d25"},{"post_id":"cktk8o6ri008kakve8zmh4jwv","tag_id":"cktk8o6t500eiakveb5k3115m","_id":"cktk8o6t700evakveaz2f2l98"},{"post_id":"cktk8o6rj008oakvef3va6zbp","tag_id":"cktk8o6t700esakve0gfc2fsv","_id":"cktk8o6t800ezakve7gso83vf"},{"post_id":"cktk8o6tt00h0akvehg5w1fi3","tag_id":"cktk8o6o20005akve3o8naofq","_id":"cktk8o6tu00h2akve6fyv32zq"},{"post_id":"cktk8o6tu00h1akve7d054dj8","tag_id":"cktk8o6of000takvegfo3ddk2","_id":"cktk8o6tv00h4akve5l835noq"},{"post_id":"cktk8o6tu00h3akve8scabvi5","tag_id":"cktk8o6ok0016akve4lvz1g74","_id":"cktk8o6tw00h7akve1g61fxqr"},{"post_id":"cktk8o6tv00h5akvefji7akri","tag_id":"cktk8o6q5004nakve0wscabgm","_id":"cktk8o6tx00haakve3p221u83"},{"post_id":"cktk8o6tw00h8akveg72k4lri","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6tz00hdakvefkef78rp"},{"post_id":"cktk8o6tx00hbakve9wq17mn4","tag_id":"cktk8o6ry00a1akve04uf69xm","_id":"cktk8o6tz00hfakve4tt1em2l"},{"post_id":"cktnqr7b6000084ve2u2250ie","tag_id":"cktnqr7f5000284ve2l7q1vwj","_id":"cktnqr7fc000384vebw3q1o9g"}],"Tag":[{"name":"51单片机","_id":"cktk8o6o20005akve3o8naofq"},{"name":"Hexo","_id":"cktk8o6of000takvegfo3ddk2"},{"name":"Java面试","_id":"cktk8o6ok0016akve4lvz1g74"},{"name":"Maven","_id":"cktk8o6om001eakvedgux10w5"},{"name":"MySQL","_id":"cktk8o6ow001takveefgsbxmx"},{"name":"spring","_id":"cktk8o6ph0030akve7x5udh45"},{"name":"Mybatis","_id":"cktk8o6q5004nakve0wscabgm"},{"name":"esp8266","_id":"cktk8o6qk005hakve2n3l2y1j"},{"name":"物联网","_id":"cktk8o6qx006nakvefl9gfr5d"},{"name":"springMVC","_id":"cktk8o6rg008eakve4ce71gs4"},{"name":"Servlet","_id":"cktk8o6rp009cakvecleqe5lf"},{"name":"Tomcat","_id":"cktk8o6ru009rakve2ntgf97s"},{"name":"javaSE","_id":"cktk8o6ry00a1akve04uf69xm"},{"name":"Http","_id":"cktk8o6s000a7akve1f33c9rq"},{"name":"win10","_id":"cktk8o6sq00ddakve9lizf5ee"},{"name":"MDK-keil5","_id":"cktk8o6sq00diakve7a9v2s38"},{"name":"stm32","_id":"cktk8o6ss00dtakvedmobezfr"},{"name":"图床","_id":"cktk8o6sy00e8akveh7cobd7s"},{"name":"矢量","_id":"cktk8o6t400edakveb0x0b1yt"},{"name":"网络通信","_id":"cktk8o6t500eiakveb5k3115m"},{"name":"网络安全","_id":"cktk8o6t700esakve0gfc2fsv"},{"name":"质数","_id":"cktnqr7f5000284ve2l7q1vwj"}]}} \ No newline at end of file diff --git a/public/service-worker.js.map b/public/service-worker.js.map index af3ed6e5..b8e37aeb 100644 --- a/public/service-worker.js.map +++ b/public/service-worker.js.map @@ -1 +1 @@ -{"version":3,"file":"service-worker.js","sources":["C:/Users/74452/AppData/Local/Temp/f03fb2651b83967bb1708a3b8059e562/service-worker.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from 'D:/quickDirs/MyBlogs/myblogs/node_modules/workbox-routing/registerRoute.mjs';\nimport {CacheFirst as workbox_strategies_CacheFirst} from 'D:/quickDirs/MyBlogs/myblogs/node_modules/workbox-strategies/CacheFirst.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from 'D:/quickDirs/MyBlogs/myblogs/node_modules/workbox-core/clientsClaim.mjs';\nimport {precacheAndRoute as workbox_precaching_precacheAndRoute} from 'D:/quickDirs/MyBlogs/myblogs/node_modules/workbox-precaching/precacheAndRoute.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n/**\n * The precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nworkbox_precaching_precacheAndRoute([\n {\n \"url\": \"404.html\",\n \"revision\": \"a7a232e84b6ac2bc2bd391a945d07824\"\n },\n {\n \"url\": \"about/index.html\",\n \"revision\": \"01da631ff03473d060a1541b8c70137c\"\n },\n {\n \"url\": \"archives/2021/02/index.html\",\n \"revision\": \"4be2e2615536a81bfb558ba5bc4a3548\"\n },\n {\n \"url\": \"archives/2021/02/page/2/index.html\",\n \"revision\": \"89b516720d32d10ef06133e6ad6b2712\"\n },\n {\n \"url\": \"archives/2021/02/page/3/index.html\",\n \"revision\": \"50736993aeab5443fb86de5fd8bb2a02\"\n },\n {\n \"url\": \"archives/2021/04/index.html\",\n \"revision\": \"21e92293745bbf86a49b40785012ac97\"\n },\n {\n \"url\": \"archives/2021/05/index.html\",\n \"revision\": \"54127fa159d932e76a90c0f95af1b851\"\n },\n {\n \"url\": \"archives/2021/06/index.html\",\n \"revision\": \"48ae0f731718f0769a634c8bccecdb6c\"\n },\n {\n \"url\": \"archives/2021/07/index.html\",\n \"revision\": \"daef3ea5d427d4b103d796380990ff32\"\n },\n {\n \"url\": \"archives/2021/07/page/2/index.html\",\n \"revision\": \"2909cc016395711523fb6e96ad26ab93\"\n },\n {\n \"url\": \"archives/2021/08/index.html\",\n \"revision\": \"7791a9d8ad163798a934a7f1b4782826\"\n },\n {\n \"url\": \"archives/2021/08/page/2/index.html\",\n \"revision\": \"4c469ac6f6107a017ea02af37daf802f\"\n },\n {\n \"url\": \"archives/2021/08/page/3/index.html\",\n \"revision\": \"89639b1284b9a7aa1c11a29d5be8d5b7\"\n },\n {\n \"url\": \"archives/2021/09/index.html\",\n \"revision\": \"f5a50ea3f7de73cd8586257903bf70a4\"\n },\n {\n \"url\": \"archives/2021/09/page/2/index.html\",\n \"revision\": \"875f4d52f04b43f702c4a13b09c2b284\"\n },\n {\n \"url\": \"archives/2021/index.html\",\n \"revision\": \"bd98a5f3ee2e9c450f7116b683c55952\"\n },\n {\n \"url\": \"archives/2021/page/10/index.html\",\n \"revision\": \"6e8ac21daa65103dec29cb928a6d62eb\"\n },\n {\n \"url\": \"archives/2021/page/11/index.html\",\n \"revision\": \"09211bdfd4eda7e45aea4e5a4a1db971\"\n },\n {\n \"url\": \"archives/2021/page/2/index.html\",\n \"revision\": \"7fd002b26e8a4cb041fea77ecadcab25\"\n },\n {\n \"url\": \"archives/2021/page/3/index.html\",\n \"revision\": \"11a307ddb2a7650152bf3dba6742536b\"\n },\n {\n \"url\": \"archives/2021/page/4/index.html\",\n \"revision\": \"98de575449557113c58f682239a8de19\"\n },\n {\n \"url\": \"archives/2021/page/5/index.html\",\n \"revision\": \"c272b4da931ac2f6fb1df67542af033b\"\n },\n {\n \"url\": \"archives/2021/page/6/index.html\",\n \"revision\": \"8edc54a2fdde6a0e06e9e1ef14f1981d\"\n },\n {\n \"url\": \"archives/2021/page/7/index.html\",\n \"revision\": \"494656a7fb1f84af7fb1087d347ec810\"\n },\n {\n \"url\": \"archives/2021/page/8/index.html\",\n \"revision\": \"e5cb67b84c6145109bd30851a0be8f2d\"\n },\n {\n \"url\": \"archives/2021/page/9/index.html\",\n \"revision\": \"bc7bdd6c186dbf1534ebad20f89d41a4\"\n },\n {\n \"url\": \"archives/index.html\",\n \"revision\": \"ec8a1ef78ff57b8075990aed0ca15c55\"\n },\n {\n \"url\": \"archives/page/10/index.html\",\n \"revision\": \"6451ddf6bface3b128fc093eebe6ae57\"\n },\n {\n \"url\": \"archives/page/11/index.html\",\n \"revision\": \"0d32873e238c2b336ce46b0ed023a7dd\"\n },\n {\n \"url\": \"archives/page/2/index.html\",\n \"revision\": \"c5d934bb77870d7a762ab0f8357dda07\"\n },\n {\n \"url\": \"archives/page/3/index.html\",\n \"revision\": \"600b089439ddda0b160f220389bf73bb\"\n },\n {\n \"url\": \"archives/page/4/index.html\",\n \"revision\": \"49a46be3d47f1713600e51265e1130d6\"\n },\n {\n \"url\": \"archives/page/5/index.html\",\n \"revision\": \"6f4d8427c1bf6e83635df6393aa28876\"\n },\n {\n \"url\": \"archives/page/6/index.html\",\n \"revision\": \"e254ace8217dc616b112c82b61651113\"\n },\n {\n \"url\": \"archives/page/7/index.html\",\n \"revision\": \"95bc8752a60ccb8f3309b76381eed4e6\"\n },\n {\n \"url\": \"archives/page/8/index.html\",\n \"revision\": \"ddcad31e4cbf696eb156c4f3aa980062\"\n },\n {\n \"url\": \"archives/page/9/index.html\",\n \"revision\": \"0d77a80308960ba468b27208dfe05dc8\"\n },\n {\n \"url\": \"assets/algolia/algoliasearch.js\",\n \"revision\": \"d5d2500bfe8443b42baaefe4996ee532\"\n },\n {\n \"url\": \"assets/algolia/algoliasearch.min.js\",\n \"revision\": \"9c5e51e57e2b1d888950bf4cb5708c49\"\n },\n {\n \"url\": \"assets/algolia/algoliasearchLite.js\",\n \"revision\": \"ce9b0e62645c036a143f639b92e7789f\"\n },\n {\n \"url\": \"assets/algolia/algoliasearchLite.min.js\",\n \"revision\": \"c2d71f042c879659dbc97f8853b62f21\"\n },\n {\n \"url\": \"categories/Hexo/index.html\",\n \"revision\": \"a808b6727235fca244f85590b3d08721\"\n },\n {\n \"url\": \"categories/index.html\",\n \"revision\": \"9629969647792ce5789f01f308340180\"\n },\n {\n \"url\": \"categories/java/index.html\",\n \"revision\": \"4a44f6d7eca9190a490ffe14a385b478\"\n },\n {\n \"url\": \"categories/java/javaEE/index.html\",\n \"revision\": \"3adef2d1ea6b9dc3cb32712faee63979\"\n },\n {\n \"url\": \"categories/java/javaSE/index.html\",\n \"revision\": \"4eca4f1a42f3ab49e881224a14fb693b\"\n },\n {\n \"url\": \"categories/java/javaSE/page/2/index.html\",\n \"revision\": \"3c61143bbadc0957c40a3311f848b327\"\n },\n {\n \"url\": \"categories/java/javaSE/page/3/index.html\",\n \"revision\": \"9573ccc8ea196a4a035c99a9da3be02e\"\n },\n {\n \"url\": \"categories/java/java面试/index.html\",\n \"revision\": \"fb3e3b068294bc17a5939250fe795703\"\n },\n {\n \"url\": \"categories/java/Maven/index.html\",\n \"revision\": \"7a30cf65bbe46865b922d0f456ca008d\"\n },\n {\n \"url\": \"categories/java/Mybatis/index.html\",\n \"revision\": \"dde00e52a2cddd6eb7af6ef3c4d94678\"\n },\n {\n \"url\": \"categories/java/MySQL/index.html\",\n \"revision\": \"c9814ade489e39cebb1912f95693eb7a\"\n },\n {\n \"url\": \"categories/java/page/2/index.html\",\n \"revision\": \"f7e1596bb44642a4c7091aeda82260d5\"\n },\n {\n \"url\": \"categories/java/page/3/index.html\",\n \"revision\": \"2d0da37ce8771d6fc2d67fb2f86aa733\"\n },\n {\n \"url\": \"categories/java/page/4/index.html\",\n \"revision\": \"7e29e939ad1201b6c8edc5b4071756c0\"\n },\n {\n \"url\": \"categories/java/page/5/index.html\",\n \"revision\": \"f284a2bad3a3f1bb72124deb8551fe8a\"\n },\n {\n \"url\": \"categories/java/page/6/index.html\",\n \"revision\": \"a177180d50d33f141027b436f5656b2c\"\n },\n {\n \"url\": \"categories/java/page/7/index.html\",\n \"revision\": \"eb003ea702cafe6683b36499f100385d\"\n },\n {\n \"url\": \"categories/java/spring/index.html\",\n \"revision\": \"5a0baae7d3c857545e0695e81168a1a2\"\n },\n {\n \"url\": \"categories/java/spring/page/2/index.html\",\n \"revision\": \"49af755ccbf31befcd34dd7217a22153\"\n },\n {\n \"url\": \"categories/java/springMVC/index.html\",\n \"revision\": \"7414094caa7e7b5ec2b7cdc661a0f09f\"\n },\n {\n \"url\": \"categories/win10/index.html\",\n \"revision\": \"b37285587971261050532ccbf19bc133\"\n },\n {\n \"url\": \"categories/杂七杂八/index.html\",\n \"revision\": \"e037f2b0417f1c7b35678a8559b83375\"\n },\n {\n \"url\": \"categories/电磁场/index.html\",\n \"revision\": \"22a57d89abf023e00e1c45b8a668e227\"\n },\n {\n \"url\": \"categories/电磁场/矢量/index.html\",\n \"revision\": \"9ac069b6f4a2086c8506d8f8a1e9a060\"\n },\n {\n \"url\": \"categories/硬件学习/51单片机/index.html\",\n \"revision\": \"fbfb0a8ba40f03ded64038c883c5b4d6\"\n },\n {\n \"url\": \"categories/硬件学习/esp8266/index.html\",\n \"revision\": \"c855e5c8f3e54ab7c2d2d11b91729245\"\n },\n {\n \"url\": \"categories/硬件学习/index.html\",\n \"revision\": \"91afb5c603a77c45b872bd412149b3c6\"\n },\n {\n \"url\": \"categories/硬件学习/page/2/index.html\",\n \"revision\": \"bcf9beb5cb97c6eb445fd69546776a4b\"\n },\n {\n \"url\": \"categories/硬件学习/page/3/index.html\",\n \"revision\": \"6d9c18a25bb08e80a7ea7a3dcb9cac2e\"\n },\n {\n \"url\": \"categories/硬件学习/stm32/index.html\",\n \"revision\": \"2fab0d8466c5db5b41c69deadbca2ef7\"\n },\n {\n \"url\": \"categories/硬件学习/物联网开发/index.html\",\n \"revision\": \"58c9d2b25d41fd982e0067e06fe0a258\"\n },\n {\n \"url\": \"categories/网络安全/index.html\",\n \"revision\": \"37b1818218b426d928d37e57de759ec5\"\n },\n {\n \"url\": \"categories/网络通信/index.html\",\n \"revision\": \"78fa0b8edf8a7ddf0962a1ae109444af\"\n },\n {\n \"url\": \"css/background.css\",\n \"revision\": \"0db0072627f1ecae81e9a4481d7537cf\"\n },\n {\n \"url\": \"css/copyright.css\",\n \"revision\": \"e5126a4122ae5bb36a3d2f56395e71dd\"\n },\n {\n \"url\": \"css/custom.css\",\n \"revision\": \"95dd2781a26bb28e4295306b0754c814\"\n },\n {\n \"url\": \"css/icon.css\",\n \"revision\": \"bef85b48e77ad67185d00ed1afbb68c4\"\n },\n {\n \"url\": \"css/index.css\",\n \"revision\": \"e5cbdaf0abd2d7167d96e1631e1737e5\"\n },\n {\n \"url\": \"css/index.min.css\",\n \"revision\": \"89a7fdf70f3d289198cee53800ad2a7d\"\n },\n {\n \"url\": \"css/mouse.css\",\n \"revision\": \"55e479af44c25819caf5ccfb0d386901\"\n },\n {\n \"url\": \"css/side.css\",\n \"revision\": \"41e18eb432f59d0d8180f15e1629ef29\"\n },\n {\n \"url\": \"css/var.css\",\n \"revision\": \"d41d8cd98f00b204e9800998ecf8427e\"\n },\n {\n \"url\": \"fonts/ajLangMan.ttf\",\n \"revision\": \"37fb83a36178b32a54ac322c274ec3c1\"\n },\n {\n \"url\": \"fonts/JetBrainsMono-Medium.woff2\",\n \"revision\": \"54b6827550ef145b4c1968518a96070f\"\n },\n {\n \"url\": \"fonts/loving.ttf\",\n \"revision\": \"570884f09afc37bc8b94223a828fdf0a\"\n },\n {\n \"url\": \"fonts/shishangblack.ttf\",\n \"revision\": \"896ca9e2117560ea1838537de1e9de07\"\n },\n {\n \"url\": \"fonts/ZhuZiAWan.woff2\",\n \"revision\": \"5d3a54462adf28c19897e7292a33e399\"\n },\n {\n \"url\": \"img/01.jpg\",\n \"revision\": \"b8404ead12c8d52bb294e13d078b71bd\"\n },\n {\n \"url\": \"img/02.jpg\",\n \"revision\": \"d875bf23886eed2f760053a5aa05df27\"\n },\n {\n \"url\": \"img/34.jpg\",\n \"revision\": \"288b5328f34706535f24c4d765b24e32\"\n },\n {\n \"url\": \"img/404.jpg\",\n \"revision\": \"4ef3cfb882b6dd4128da4c8745e9a507\"\n },\n {\n \"url\": \"img/algolia.svg\",\n \"revision\": \"fd40b88ac5370a5353a50b8175c1f367\"\n },\n {\n \"url\": \"img/articles.png\",\n \"revision\": \"fcc028ac9366f8c15741a8d77c866155\"\n },\n {\n \"url\": \"img/articles2.png\",\n \"revision\": \"95ae3b30ec41fb9c8bcdd06f0c294c09\"\n },\n {\n \"url\": \"img/avatar1.jpg\",\n \"revision\": \"6e744e5f778210f988b700018efa34f9\"\n },\n {\n \"url\": \"img/avatar2.jpg\",\n \"revision\": \"30b9763713fa2e81f58fae6f34e40e50\"\n },\n {\n \"url\": \"img/banner.png\",\n \"revision\": \"95124ebf7e6ed51ac65f80e92c25c4b0\"\n },\n {\n \"url\": \"img/blissed01.jpg\",\n \"revision\": \"f57e19c138f825686586f3f9ebae05b4\"\n },\n {\n \"url\": \"img/categories.png\",\n \"revision\": \"85601915a6dec609d7d56a98e8413ea9\"\n },\n {\n \"url\": \"img/favicon.png\",\n \"revision\": \"7a8c47cb5a2149c1a1af21e90ecd9ca7\"\n },\n {\n \"url\": \"img/friend_404.gif\",\n \"revision\": \"68af0be9d22722e74665ef44dd532ba8\"\n },\n {\n \"url\": \"img/huiji.png\",\n \"revision\": \"82d4c602b2f634778630ce5d02a6a4b3\"\n },\n {\n \"url\": \"img/mybatis.png\",\n \"revision\": \"91a2c74cf29863926bd9a6003437cf93\"\n },\n {\n \"url\": \"img/mybatis1.png\",\n \"revision\": \"ca514cda6fb6d3c406d7eb8a3e27ec58\"\n },\n {\n \"url\": \"img/pcbimg.png\",\n \"revision\": \"65d32a5066507f9b57067359cb75c0ad\"\n },\n {\n \"url\": \"img/pcbimg2.png\",\n \"revision\": \"00a107a9e97df662e8121929d11afcf9\"\n },\n {\n \"url\": \"img/plane.png\",\n \"revision\": \"8806f57b1ef84a430207b4223b0c1072\"\n },\n {\n \"url\": \"img/plane1.png\",\n \"revision\": \"f0e88e80119eb50873fed11ce4ba168b\"\n },\n {\n \"url\": \"index.html\",\n \"revision\": \"e71fd0a9ec45e9cdeca6986e14972b8c\"\n },\n {\n \"url\": \"js/main.js\",\n \"revision\": \"01f62452fd05335569c6341d3ac0f52b\"\n },\n {\n \"url\": \"js/search/algolia.js\",\n \"revision\": \"533d980c0d50a0d0d7fe34c41a3e2100\"\n },\n {\n \"url\": \"js/search/local-search.js\",\n \"revision\": \"acb62dcdf7e90930da3f6bf07349fc21\"\n },\n {\n \"url\": \"js/tw_cn.js\",\n \"revision\": \"b3810513e04b13b2d18c6b779c883f85\"\n },\n {\n \"url\": \"js/utils.js\",\n \"revision\": \"12cef07c2e9bc8841a5380df4fd342f5\"\n },\n {\n \"url\": \"link/index.html\",\n \"revision\": \"165b69defbf969aa80c55aaf02b9287a\"\n },\n {\n \"url\": \"live2d-widget/assets/screenshot-1.png\",\n \"revision\": \"30b70e6cd9be9812adcb347536f0da85\"\n },\n {\n \"url\": \"live2d-widget/assets/screenshot-2.png\",\n \"revision\": \"1295844e29a6d6dc3a4aa0db8faa7da7\"\n },\n {\n \"url\": \"live2d-widget/assets/screenshot-3.png\",\n \"revision\": \"4aa1995daf77bc19803648fe6a65c33e\"\n },\n {\n \"url\": \"live2d-widget/autoload.js\",\n \"revision\": \"78870afb2355b97450ea1f93bc8dcad8\"\n },\n {\n \"url\": \"live2d-widget/demo/demo.html\",\n \"revision\": \"2596a8630c0801002b3dff127b50518b\"\n },\n {\n \"url\": \"live2d-widget/demo/login.html\",\n \"revision\": \"6790fe17ee0264f77ba972c941f5d4c3\"\n },\n {\n \"url\": \"live2d-widget/live2d.min.js\",\n \"revision\": \"ee7efff8ff5d1d4bd4a0ff99affd3ec7\"\n },\n {\n \"url\": \"live2d-widget/README.html\",\n \"revision\": \"3d7233f7971913d0ec0f363a14a11cc3\"\n },\n {\n \"url\": \"live2d-widget/waifu-tips.js\",\n \"revision\": \"e01c75f70a9465389471f638b1356bf8\"\n },\n {\n \"url\": \"live2d-widget/waifu.css\",\n \"revision\": \"caedcaf7be20449a974a9eb39e51f259\"\n },\n {\n \"url\": \"music/index.html\",\n \"revision\": \"630d55b1649e3a8728cae81406473cef\"\n },\n {\n \"url\": \"notes/index.html\",\n \"revision\": \"d9fa8fceea30b856762119ac6565c36a\"\n },\n {\n \"url\": \"page/10/index.html\",\n \"revision\": \"1b3c27bb0de5fc0af0af0a3f7d70c9a0\"\n },\n {\n \"url\": \"page/11/index.html\",\n \"revision\": \"8eee013a3809f0399ef8e45c8f2bbb75\"\n },\n {\n \"url\": \"page/2/index.html\",\n \"revision\": \"d89ab174955bfcf24d37602b7e1b1d48\"\n },\n {\n \"url\": \"page/3/index.html\",\n \"revision\": \"e317d50ac8225fe970bda30f0448848e\"\n },\n {\n \"url\": \"page/4/index.html\",\n \"revision\": \"52de6106b999a1dd1b59fc27e284d1f3\"\n },\n {\n \"url\": \"page/5/index.html\",\n \"revision\": \"1a314a8f38f7a84fc9b55f8789b72b5e\"\n },\n {\n \"url\": \"page/6/index.html\",\n \"revision\": \"9df266563df8db5b2e9df3fe7ead35f3\"\n },\n {\n \"url\": \"page/7/index.html\",\n \"revision\": \"9fba2df0fe53ae2f5bd738f1284d8a05\"\n },\n {\n \"url\": \"page/8/index.html\",\n \"revision\": \"44cce65a7a6b3c4f0da2e334e403149d\"\n },\n {\n \"url\": \"page/9/index.html\",\n \"revision\": \"a120e1c07258790b920206d2a45530a5\"\n },\n {\n \"url\": \"poems/index.html\",\n \"revision\": \"4210e51c9ae8f76897dff990a5ce417c\"\n },\n {\n \"url\": \"posts/1294a4bd.html\",\n \"revision\": \"81333aa36cd5165d6851e7e8d6fe503c\"\n },\n {\n \"url\": \"posts/170e1590.html\",\n \"revision\": \"a16954dd2c172396699d1fb277da432b\"\n },\n {\n \"url\": \"posts/17528481.html\",\n \"revision\": \"678cbfce953ba7f81523fa9d318fc1c5\"\n },\n {\n \"url\": \"posts/190b30be.html\",\n \"revision\": \"2e824349660a0b8093114e07eec88454\"\n },\n {\n \"url\": \"posts/1a57c6b6.html\",\n \"revision\": \"b4d82dae1cce2d27269aef19630a00a4\"\n },\n {\n \"url\": \"posts/1a9b2152.html\",\n \"revision\": \"e4442573ec1ee22f0e58dc75822dd8fc\"\n },\n {\n \"url\": \"posts/1b6a7930.html\",\n \"revision\": \"50bdbe737020dde91825222da3d1f88d\"\n },\n {\n \"url\": \"posts/1cd3002f.html\",\n \"revision\": \"b997e719ac7cb9d5c52b00ef684ead58\"\n },\n {\n \"url\": \"posts/1eb33081.html\",\n \"revision\": \"1ac0c21f9815442b445a6f3ea12fcd48\"\n },\n {\n \"url\": \"posts/2080031a.html\",\n \"revision\": \"0bc32d3ef9eca7b676cbea4e694377de\"\n },\n {\n \"url\": \"posts/26c04051.html\",\n \"revision\": \"bdff62ac983eb6da35598d64e5bcbfd0\"\n },\n {\n \"url\": \"posts/27153554.html\",\n \"revision\": \"f9b8b6911de198365061d2cb718032fa\"\n },\n {\n \"url\": \"posts/2a3cf99e.html\",\n \"revision\": \"b43b33474a4b8ddd076ab0a80018a969\"\n },\n {\n \"url\": \"posts/2d7f83ef.html\",\n \"revision\": \"da4485768402f792f25d8096f0bfcddf\"\n },\n {\n \"url\": \"posts/345d8e17.html\",\n \"revision\": \"78ce196f3745403e9a349c02cf5a5c95\"\n },\n {\n \"url\": \"posts/374aa28c.html\",\n \"revision\": \"cf8a4a3ac623119649cfee8fb68ee8a7\"\n },\n {\n \"url\": \"posts/383e39e3.html\",\n \"revision\": \"6200a083d5271eb6721ab84e281d73cf\"\n },\n {\n \"url\": \"posts/38b36403.html\",\n \"revision\": \"b398539bd960298dacaf84bde1d17998\"\n },\n {\n \"url\": \"posts/38b3e585.html\",\n \"revision\": \"1a665f060b1a531fa132909b31fced64\"\n },\n {\n \"url\": \"posts/39aef30f.html\",\n \"revision\": \"a08488bfba824945e69fbdca45e7a64a\"\n },\n {\n \"url\": \"posts/39f00355.html\",\n \"revision\": \"5b252f4d1ea144a1dacc8a6d7c8100c2\"\n },\n {\n \"url\": \"posts/3aac01ca.html\",\n \"revision\": \"6f1b62b3a9ae83a21869d361ef356978\"\n },\n {\n \"url\": \"posts/3afd7950.html\",\n \"revision\": \"969f4e8a18cdebcee45c09a048298e89\"\n },\n {\n \"url\": \"posts/3b32a20d.html\",\n \"revision\": \"d7c205e3801abe40dd2f68cf6060613d\"\n },\n {\n \"url\": \"posts/3de0f4a2.html\",\n \"revision\": \"21b5182fcad8e0d134f19768739948f7\"\n },\n {\n \"url\": \"posts/3f4cddeb.html\",\n \"revision\": \"9cf13103a03369585c298ad7ebc2aeef\"\n },\n {\n \"url\": \"posts/3fb3b16d.html\",\n \"revision\": \"a1c16cee763f593560cc9da0732212f1\"\n },\n {\n \"url\": \"posts/3feecb5e.html\",\n \"revision\": \"92d687ccafaef802800a3f834a184d15\"\n },\n {\n \"url\": \"posts/401491fc.html\",\n \"revision\": \"e49b8431a4e750a44c7361bab4841f4f\"\n },\n {\n \"url\": \"posts/446faab7.html\",\n \"revision\": \"6f1df82f5af9d78d2edd056e53a6199e\"\n },\n {\n \"url\": \"posts/4515cdfc.html\",\n \"revision\": \"6bb68ffeee9ca680205ee0651ff2cab5\"\n },\n {\n \"url\": \"posts/4609d38b.html\",\n \"revision\": \"c16546cad73f1c073a0feda1b3c5770d\"\n },\n {\n \"url\": \"posts/46cd19c8.html\",\n \"revision\": \"b909b086d486c22cfb1be445f8291fb7\"\n },\n {\n \"url\": \"posts/4dc0a7a8.html\",\n \"revision\": \"664927110cdf02f2e5ccc68d130e8429\"\n },\n {\n \"url\": \"posts/4f0e7b50.html\",\n \"revision\": \"fb90e5d3cb4bf700ad504a09d4f2a672\"\n },\n {\n \"url\": \"posts/51a2ab89.html\",\n \"revision\": \"2950dbc224cf5132c9b94d8865688e01\"\n },\n {\n \"url\": \"posts/52b44ec7.html\",\n \"revision\": \"53b5850a69cc0949f4b18101998714ee\"\n },\n {\n \"url\": \"posts/52da8eea.html\",\n \"revision\": \"4da011e6fcfd8608c135a32014500856\"\n },\n {\n \"url\": \"posts/52e2375.html\",\n \"revision\": \"e1f6d3ab2d0c3a47a3c60b0d017f019d\"\n },\n {\n \"url\": \"posts/530cfffd.html\",\n \"revision\": \"af4e2dc316ffd91a7c1ba60d35058af4\"\n },\n {\n \"url\": \"posts/54667693.html\",\n \"revision\": \"f7776dd9f459df57c11a1a8ec791610d\"\n },\n {\n \"url\": \"posts/54a4218.html\",\n \"revision\": \"92aea03b1cd72fcc2d4c9dd0a1ffdb76\"\n },\n {\n \"url\": \"posts/57ac54fe.html\",\n \"revision\": \"716b6dca031440e2dc4cd2c839f83219\"\n },\n {\n \"url\": \"posts/59e405f3.html\",\n \"revision\": \"8d8e56c187d9dd4a88cb60be40d50383\"\n },\n {\n \"url\": \"posts/5dd9c9e3.html\",\n \"revision\": \"4e6e74efed1d8538479c030d24e01a9f\"\n },\n {\n \"url\": \"posts/5e64b19a.html\",\n \"revision\": \"818a275d5b85d6a3c0f91fba1895b171\"\n },\n {\n \"url\": \"posts/63036c3c.html\",\n \"revision\": \"270d87c87ec356e692d2036171683add\"\n },\n {\n \"url\": \"posts/683d1a78.html\",\n \"revision\": \"cddeb331870512451d1c21b71ac9ee03\"\n },\n {\n \"url\": \"posts/68f2c599.html\",\n \"revision\": \"081a44a5e7cab270e72b839ec2d58c47\"\n },\n {\n \"url\": \"posts/6e81c43e.html\",\n \"revision\": \"ba35351b9ba3b2aaecf90068700d75f3\"\n },\n {\n \"url\": \"posts/73ba8569.html\",\n \"revision\": \"010429a34d4ed5e1ee5b2b154e3f3fe9\"\n },\n {\n \"url\": \"posts/752d4bdb.html\",\n \"revision\": \"de7f8ef1210aa5684ab48f528010b21f\"\n },\n {\n \"url\": \"posts/753dfa3e.html\",\n \"revision\": \"887364f41e068a2f0f96dc7c520b66b9\"\n },\n {\n \"url\": \"posts/78903d87.html\",\n \"revision\": \"414f9ca5c71201e489b69efe9f0709c3\"\n },\n {\n \"url\": \"posts/79c0e989.html\",\n \"revision\": \"f67181c4b387b3b15caaac430c9e273a\"\n },\n {\n \"url\": \"posts/7dc1e032.html\",\n \"revision\": \"36258e645dfc75aa0145ce8bce45e727\"\n },\n {\n \"url\": \"posts/7f2244ba.html\",\n \"revision\": \"63bc2cbe9badee1cf2e7e81e51710c7e\"\n },\n {\n \"url\": \"posts/81ecdf4a.html\",\n \"revision\": \"e35dfc6f48553503ed0d47db3df74c08\"\n },\n {\n \"url\": \"posts/88ad757.html\",\n \"revision\": \"dddf77dc281e66b3e07096b09254fe26\"\n },\n {\n \"url\": \"posts/890c8bdb.html\",\n \"revision\": \"92d1bd1a8ae7ce9b65708fe32e2b4170\"\n },\n {\n \"url\": \"posts/894e18ee.html\",\n \"revision\": \"80c8ca83819c5e0fe7733e6c29c049de\"\n },\n {\n \"url\": \"posts/89cf4bca.html\",\n \"revision\": \"86850687a5013204529d6a50155e1467\"\n },\n {\n \"url\": \"posts/8a45e6a3.html\",\n \"revision\": \"d3b34d8a029c03b265f52cd93a59f6ec\"\n },\n {\n \"url\": \"posts/8b75069d.html\",\n \"revision\": \"865310c8cc59577a31e1f4e892bd0ad4\"\n },\n {\n \"url\": \"posts/94566522.html\",\n \"revision\": \"ef99600a1d5400206f02592a2cf6cdef\"\n },\n {\n \"url\": \"posts/9dc8e4af.html\",\n \"revision\": \"ae00041a27fd9df8b1091e54c5987c83\"\n },\n {\n \"url\": \"posts/9e0a8624.html\",\n \"revision\": \"97a0b205072e4d7507115fab14a877fb\"\n },\n {\n \"url\": \"posts/a1a1eb5.html\",\n \"revision\": \"522417ac47ead0748a6dc7133e2a979b\"\n },\n {\n \"url\": \"posts/a4682555.html\",\n \"revision\": \"2501e158602845b9e8837ce45b02debe\"\n },\n {\n \"url\": \"posts/a4c109bd.html\",\n \"revision\": \"7906b2c2ae9e76bcb31ba2f29483b103\"\n },\n {\n \"url\": \"posts/ab09b476.html\",\n \"revision\": \"c6175b8f81aab232f77e50ea2deded95\"\n },\n {\n \"url\": \"posts/ab69c7ec.html\",\n \"revision\": \"0e48992feee907264ea828f04bb599ee\"\n },\n {\n \"url\": \"posts/adfcabaa.html\",\n \"revision\": \"f9b605b4084730de310182ac4c2ab766\"\n },\n {\n \"url\": \"posts/af3c95f0.html\",\n \"revision\": \"2a3f2046efc354662f15abed84e7667e\"\n },\n {\n \"url\": \"posts/b3cec184.html\",\n \"revision\": \"dd30ff1b7e08376b003ab828f51fbf65\"\n },\n {\n \"url\": \"posts/b54f36d8.html\",\n \"revision\": \"9b14bff1930d86c722b618871576a216\"\n },\n {\n \"url\": \"posts/b836e65.html\",\n \"revision\": \"25fb2b122cce636c35b3e273219e7e74\"\n },\n {\n \"url\": \"posts/b8426075.html\",\n \"revision\": \"3cfe512860fbdbd59b77bee85d5b2534\"\n },\n {\n \"url\": \"posts/b9bb0bd5.html\",\n \"revision\": \"bd164afaaa887922d7c9eda27fc016b6\"\n },\n {\n \"url\": \"posts/ba43a8dc.html\",\n \"revision\": \"bc0528f3680e54b6345fb472b92145db\"\n },\n {\n \"url\": \"posts/baad5185.html\",\n \"revision\": \"0e1e366409a5bf5419d9df1e1756b22d\"\n },\n {\n \"url\": \"posts/bdfc8ea1.html\",\n \"revision\": \"eab73914239cbb16d479578c59e1c30e\"\n },\n {\n \"url\": \"posts/be39d4e8.html\",\n \"revision\": \"5d86128c08d2d0745c89929ef5fcc6ff\"\n },\n {\n \"url\": \"posts/c0626013.html\",\n \"revision\": \"4a3846afe8928b58580c8d84d77df401\"\n },\n {\n \"url\": \"posts/c25015c5.html\",\n \"revision\": \"75c735dda5702355ac81ac3682b7cc9a\"\n },\n {\n \"url\": \"posts/c27dd6ec.html\",\n \"revision\": \"635767f606154f5de863b9e1aed2f4ac\"\n },\n {\n \"url\": \"posts/c2d1d169.html\",\n \"revision\": \"e77d3649769f351d4ae8003d68e00ea3\"\n },\n {\n \"url\": \"posts/c4d68191.html\",\n \"revision\": \"aa7d0142581a5b69c52df299310639f9\"\n },\n {\n \"url\": \"posts/c5d59ae.html\",\n \"revision\": \"7a6e7e12c36daea4eefc748ca4cb4f6a\"\n },\n {\n \"url\": \"posts/c63fec2a.html\",\n \"revision\": \"92a841638671c97ac5d6d9dcf9d5e01b\"\n },\n {\n \"url\": \"posts/c8eb4af9.html\",\n \"revision\": \"c868f029ca0c67f8835954a3e1fc6d5a\"\n },\n {\n \"url\": \"posts/c9f91dab.html\",\n \"revision\": \"c408b250c599bf1e28b119a1a49a2a7c\"\n },\n {\n \"url\": \"posts/d150c3f1.html\",\n \"revision\": \"44d484b7f5066c9a335e6ec3bb89d389\"\n },\n {\n \"url\": \"posts/d29e4d17.html\",\n \"revision\": \"c269f6cbec540242834d5724b2251cf4\"\n },\n {\n \"url\": \"posts/d709dda.html\",\n \"revision\": \"c03d77a3ab538cef7c4a9a0c81597942\"\n },\n {\n \"url\": \"posts/d8642fb9.html\",\n \"revision\": \"1858617b76b9729b1a3f45f3d4223a9f\"\n },\n {\n \"url\": \"posts/d959138b.html\",\n \"revision\": \"86ea256c08936582f6d2d0b57d1b0c00\"\n },\n {\n \"url\": \"posts/dc7f8ae1.html\",\n \"revision\": \"58a97cec2c52b339dd2f0bd592b08f3f\"\n },\n {\n \"url\": \"posts/e0bbd322.html\",\n \"revision\": \"32fa8fc14c5cf2e9de3b0ad408ff00dc\"\n },\n {\n \"url\": \"posts/e7ba7594.html\",\n \"revision\": \"6bf2be8d9c809f0de312de6a1fa663aa\"\n },\n {\n \"url\": \"posts/ecec7d3f.html\",\n \"revision\": \"8eade11b113ff3409e2eaaf6e09301d5\"\n },\n {\n \"url\": \"posts/ee4b2b4d.html\",\n \"revision\": \"e6d1ae3ee12ee60fb04af277fc4a7ab0\"\n },\n {\n \"url\": \"posts/f218ca42.html\",\n \"revision\": \"a84d122a809618d143a9a8f4b3eaa443\"\n },\n {\n \"url\": \"posts/f5c95c.html\",\n \"revision\": \"f12e497f0aa22e4f7fd5e188af1b5be8\"\n },\n {\n \"url\": \"posts/f68104ec.html\",\n \"revision\": \"c31a7e027b3b44fabf03981d50a61fab\"\n },\n {\n \"url\": \"posts/f8dae6c5.html\",\n \"revision\": \"c7666db6597bb22209c139e9640bb88e\"\n },\n {\n \"url\": \"tags/51单片机/index.html\",\n \"revision\": \"f08e8b5ffe0624ddfaa775b6d4b21cba\"\n },\n {\n \"url\": \"tags/esp8266/index.html\",\n \"revision\": \"8b3b3f3c8b6d0e536a7cfb9a6248e4ef\"\n },\n {\n \"url\": \"tags/esp8266/page/2/index.html\",\n \"revision\": \"2bb02c968eb52ff4904a4e2cd5683c5e\"\n },\n {\n \"url\": \"tags/Hexo/index.html\",\n \"revision\": \"5bd55b99cb98c4c5ddc51786dbe669a7\"\n },\n {\n \"url\": \"tags/Http/index.html\",\n \"revision\": \"3753cd765a9b96a6d042da45da703b5a\"\n },\n {\n \"url\": \"tags/index.html\",\n \"revision\": \"dcec168a6e990064d105694b27e428d9\"\n },\n {\n \"url\": \"tags/javaSE/index.html\",\n \"revision\": \"18e46ba4c1c114d36aa599b8542c158b\"\n },\n {\n \"url\": \"tags/javaSE/page/2/index.html\",\n \"revision\": \"01a38fdaf0ded022d440c810ffb2c627\"\n },\n {\n \"url\": \"tags/javaSE/page/3/index.html\",\n \"revision\": \"41722f9b5d82fd33bf350e85e45d4565\"\n },\n {\n \"url\": \"tags/Java面试/index.html\",\n \"revision\": \"44727da649352de86dec2661d9d7f979\"\n },\n {\n \"url\": \"tags/Maven/index.html\",\n \"revision\": \"436777f83ac11b3f2a5d05392d5f1e19\"\n },\n {\n \"url\": \"tags/MDK-keil5/index.html\",\n \"revision\": \"32cf6e202f1221ccd47ff86b6c73f6ef\"\n },\n {\n \"url\": \"tags/Mybatis/index.html\",\n \"revision\": \"9e8a4aa0292b81a86d45b71f2355817e\"\n },\n {\n \"url\": \"tags/MySQL/index.html\",\n \"revision\": \"17db5afc6d504a888120538274ddca26\"\n },\n {\n \"url\": \"tags/Servlet/index.html\",\n \"revision\": \"b69131c5be9ba12355cfe5f17e7e20cf\"\n },\n {\n \"url\": \"tags/spring/index.html\",\n \"revision\": \"7f9c5cd25a795326b801cc7c3b966e9c\"\n },\n {\n \"url\": \"tags/spring/page/2/index.html\",\n \"revision\": \"909617a5c64e4196e8262be893f0a40f\"\n },\n {\n \"url\": \"tags/springMVC/index.html\",\n \"revision\": \"74f24b15fb21549e8bbac15db772fa78\"\n },\n {\n \"url\": \"tags/stm32/index.html\",\n \"revision\": \"cd05b8ad3ca71fd8681aeb888a2f3f10\"\n },\n {\n \"url\": \"tags/Tomcat/index.html\",\n \"revision\": \"c35de9c4fcac9aa3133d0cfdd38ec717\"\n },\n {\n \"url\": \"tags/win10/index.html\",\n \"revision\": \"4c7dfbe269f0040fc0ab162af8f6e58b\"\n },\n {\n \"url\": \"tags/图床/index.html\",\n \"revision\": \"c03fcc5f4f14ac8b7dd942498dd54efd\"\n },\n {\n \"url\": \"tags/物联网/index.html\",\n \"revision\": \"1b9368d355eb19851e83f73e1d975c7d\"\n },\n {\n \"url\": \"tags/矢量/index.html\",\n \"revision\": \"a8657e2fe91b20c04cc43cc80021b35c\"\n },\n {\n \"url\": \"tags/网络安全/index.html\",\n \"revision\": \"3bfea466ec8b0380de196387d6f25801\"\n },\n {\n \"url\": \"tags/网络通信/index.html\",\n \"revision\": \"734785869d51c2eadb6d23fab19f5458\"\n },\n {\n \"url\": \"web-links/index.html\",\n \"revision\": \"fe36137d3417b0550f1737f1014eadd4\"\n }\n], {});\n\n\n\n\nworkbox_routing_registerRoute(/^https:\\/\\/cdn\\.example\\.com\\/.*/, new workbox_strategies_CacheFirst(), 'GET');\n\n\n\n\n"],"names":["self","skipWaiting","workbox_strategies_CacheFirst"],"mappings":"szBAsBAA,KAAKC,kDAU+B,CAClC,KACS,oBACK,oCAEd,KACS,4BACK,oCAEd,KACS,uCACK,oCAEd,KACS,8CACK,oCAEd,KACS,8CACK,oCAEd,KACS,uCACK,oCAEd,KACS,uCACK,oCAEd,KACS,uCACK,oCAEd,KACS,uCACK,oCAEd,KACS,8CACK,oCAEd,KACS,uCACK,oCAEd,KACS,8CACK,oCAEd,KACS,8CACK,oCAEd,KACS,uCACK,oCAEd,KACS,8CACK,oCAEd,KACS,oCACK,oCAEd,KACS,4CACK,oCAEd,KACS,4CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,+BACK,oCAEd,KACS,uCACK,oCAEd,KACS,uCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,2CACK,oCAEd,KACS,+CACK,oCAEd,KACS,+CACK,oCAEd,KACS,mDACK,oCAEd,KACS,sCACK,oCAEd,KACS,iCACK,oCAEd,KACS,sCACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,oDACK,oCAEd,KACS,oDACK,oCAEd,KACS,6CACK,oCAEd,KACS,4CACK,oCAEd,KACS,8CACK,oCAEd,KACS,4CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,oDACK,oCAEd,KACS,gDACK,oCAEd,KACS,uCACK,oCAEd,KACS,sCACK,oCAEd,KACS,qCACK,oCAEd,KACS,wCACK,oCAEd,KACS,4CACK,oCAEd,KACS,8CACK,oCAEd,KACS,sCACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,4CACK,oCAEd,KACS,4CACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,8BACK,oCAEd,KACS,6BACK,oCAEd,KACS,0BACK,oCAEd,KACS,wBACK,oCAEd,KACS,yBACK,oCAEd,KACS,6BACK,oCAEd,KACS,yBACK,oCAEd,KACS,wBACK,oCAEd,KACS,uBACK,oCAEd,KACS,+BACK,oCAEd,KACS,4CACK,oCAEd,KACS,4BACK,oCAEd,KACS,mCACK,oCAEd,KACS,iCACK,oCAEd,KACS,sBACK,oCAEd,KACS,sBACK,oCAEd,KACS,sBACK,oCAEd,KACS,uBACK,oCAEd,KACS,2BACK,oCAEd,KACS,4BACK,oCAEd,KACS,6BACK,oCAEd,KACS,2BACK,oCAEd,KACS,2BACK,oCAEd,KACS,0BACK,oCAEd,KACS,6BACK,oCAEd,KACS,8BACK,oCAEd,KACS,2BACK,oCAEd,KACS,8BACK,oCAEd,KACS,yBACK,oCAEd,KACS,2BACK,oCAEd,KACS,4BACK,oCAEd,KACS,0BACK,oCAEd,KACS,2BACK,oCAEd,KACS,yBACK,oCAEd,KACS,0BACK,oCAEd,KACS,sBACK,oCAEd,KACS,sBACK,oCAEd,KACS,gCACK,oCAEd,KACS,qCACK,oCAEd,KACS,uBACK,oCAEd,KACS,uBACK,oCAEd,KACS,2BACK,oCAEd,KACS,iDACK,oCAEd,KACS,iDACK,oCAEd,KACS,iDACK,oCAEd,KACS,qCACK,oCAEd,KACS,wCACK,oCAEd,KACS,yCACK,oCAEd,KACS,uCACK,oCAEd,KACS,qCACK,oCAEd,KACS,uCACK,oCAEd,KACS,mCACK,oCAEd,KACS,4BACK,oCAEd,KACS,4BACK,oCAEd,KACS,8BACK,oCAEd,KACS,8BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,4BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,6BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,iCACK,oCAEd,KACS,mCACK,oCAEd,KACS,0CACK,oCAEd,KACS,gCACK,oCAEd,KACS,gCACK,oCAEd,KACS,2BACK,oCAEd,KACS,kCACK,oCAEd,KACS,yCACK,oCAEd,KACS,yCACK,oCAEd,KACS,kCACK,oCAEd,KACS,iCACK,oCAEd,KACS,qCACK,oCAEd,KACS,mCACK,oCAEd,KACS,iCACK,oCAEd,KACS,mCACK,oCAEd,KACS,kCACK,oCAEd,KACS,yCACK,oCAEd,KACS,qCACK,oCAEd,KACS,iCACK,oCAEd,KACS,kCACK,oCAEd,KACS,iCACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,gCACK,oCAEd,KACS,gCACK,oCAEd,KACS,gCACK,qCAEb,oBAK2B,mCAAoC,IAAIC,aAAiC"} \ No newline at end of file +{"version":3,"file":"service-worker.js","sources":["C:/Users/74452/AppData/Local/Temp/e352ab679dbfcbd127f4990c0c2a1a6d/service-worker.js"],"sourcesContent":["import {registerRoute as workbox_routing_registerRoute} from 'D:/quickDirs/MyBlogs/myblogs/node_modules/workbox-routing/registerRoute.mjs';\nimport {CacheFirst as workbox_strategies_CacheFirst} from 'D:/quickDirs/MyBlogs/myblogs/node_modules/workbox-strategies/CacheFirst.mjs';\nimport {clientsClaim as workbox_core_clientsClaim} from 'D:/quickDirs/MyBlogs/myblogs/node_modules/workbox-core/clientsClaim.mjs';\nimport {precacheAndRoute as workbox_precaching_precacheAndRoute} from 'D:/quickDirs/MyBlogs/myblogs/node_modules/workbox-precaching/precacheAndRoute.mjs';/**\n * Welcome to your Workbox-powered service worker!\n *\n * You'll need to register this file in your web app.\n * See https://goo.gl/nhQhGp\n *\n * The rest of the code is auto-generated. Please don't update this file\n * directly; instead, make changes to your Workbox build configuration\n * and re-run your build process.\n * See https://goo.gl/2aRDsh\n */\n\n\n\n\n\n\n\n\nself.skipWaiting();\n\nworkbox_core_clientsClaim();\n\n\n/**\n * The precacheAndRoute() method efficiently caches and responds to\n * requests for URLs in the manifest.\n * See https://goo.gl/S9QRab\n */\nworkbox_precaching_precacheAndRoute([\n {\n \"url\": \"404.html\",\n \"revision\": \"a7a232e84b6ac2bc2bd391a945d07824\"\n },\n {\n \"url\": \"about/index.html\",\n \"revision\": \"01da631ff03473d060a1541b8c70137c\"\n },\n {\n \"url\": \"archives/2021/02/index.html\",\n \"revision\": \"4be2e2615536a81bfb558ba5bc4a3548\"\n },\n {\n \"url\": \"archives/2021/02/page/2/index.html\",\n \"revision\": \"89b516720d32d10ef06133e6ad6b2712\"\n },\n {\n \"url\": \"archives/2021/02/page/3/index.html\",\n \"revision\": \"50736993aeab5443fb86de5fd8bb2a02\"\n },\n {\n \"url\": \"archives/2021/04/index.html\",\n \"revision\": \"21e92293745bbf86a49b40785012ac97\"\n },\n {\n \"url\": \"archives/2021/05/index.html\",\n \"revision\": \"54127fa159d932e76a90c0f95af1b851\"\n },\n {\n \"url\": \"archives/2021/06/index.html\",\n \"revision\": \"48ae0f731718f0769a634c8bccecdb6c\"\n },\n {\n \"url\": \"archives/2021/07/index.html\",\n \"revision\": \"daef3ea5d427d4b103d796380990ff32\"\n },\n {\n \"url\": \"archives/2021/07/page/2/index.html\",\n \"revision\": \"2909cc016395711523fb6e96ad26ab93\"\n },\n {\n \"url\": \"archives/2021/08/index.html\",\n \"revision\": \"7791a9d8ad163798a934a7f1b4782826\"\n },\n {\n \"url\": \"archives/2021/08/page/2/index.html\",\n \"revision\": \"4c469ac6f6107a017ea02af37daf802f\"\n },\n {\n \"url\": \"archives/2021/08/page/3/index.html\",\n \"revision\": \"89639b1284b9a7aa1c11a29d5be8d5b7\"\n },\n {\n \"url\": \"archives/2021/09/index.html\",\n \"revision\": \"f5a50ea3f7de73cd8586257903bf70a4\"\n },\n {\n \"url\": \"archives/2021/09/page/2/index.html\",\n \"revision\": \"875f4d52f04b43f702c4a13b09c2b284\"\n },\n {\n \"url\": \"archives/2021/index.html\",\n \"revision\": \"bd98a5f3ee2e9c450f7116b683c55952\"\n },\n {\n \"url\": \"archives/2021/page/10/index.html\",\n \"revision\": \"6e8ac21daa65103dec29cb928a6d62eb\"\n },\n {\n \"url\": \"archives/2021/page/11/index.html\",\n \"revision\": \"09211bdfd4eda7e45aea4e5a4a1db971\"\n },\n {\n \"url\": \"archives/2021/page/2/index.html\",\n \"revision\": \"7fd002b26e8a4cb041fea77ecadcab25\"\n },\n {\n \"url\": \"archives/2021/page/3/index.html\",\n \"revision\": \"11a307ddb2a7650152bf3dba6742536b\"\n },\n {\n \"url\": \"archives/2021/page/4/index.html\",\n \"revision\": \"98de575449557113c58f682239a8de19\"\n },\n {\n \"url\": \"archives/2021/page/5/index.html\",\n \"revision\": \"c272b4da931ac2f6fb1df67542af033b\"\n },\n {\n \"url\": \"archives/2021/page/6/index.html\",\n \"revision\": \"8edc54a2fdde6a0e06e9e1ef14f1981d\"\n },\n {\n \"url\": \"archives/2021/page/7/index.html\",\n \"revision\": \"494656a7fb1f84af7fb1087d347ec810\"\n },\n {\n \"url\": \"archives/2021/page/8/index.html\",\n \"revision\": \"e5cb67b84c6145109bd30851a0be8f2d\"\n },\n {\n \"url\": \"archives/2021/page/9/index.html\",\n \"revision\": \"bc7bdd6c186dbf1534ebad20f89d41a4\"\n },\n {\n \"url\": \"archives/index.html\",\n \"revision\": \"ec8a1ef78ff57b8075990aed0ca15c55\"\n },\n {\n \"url\": \"archives/page/10/index.html\",\n \"revision\": \"6451ddf6bface3b128fc093eebe6ae57\"\n },\n {\n \"url\": \"archives/page/11/index.html\",\n \"revision\": \"0d32873e238c2b336ce46b0ed023a7dd\"\n },\n {\n \"url\": \"archives/page/2/index.html\",\n \"revision\": \"c5d934bb77870d7a762ab0f8357dda07\"\n },\n {\n \"url\": \"archives/page/3/index.html\",\n \"revision\": \"600b089439ddda0b160f220389bf73bb\"\n },\n {\n \"url\": \"archives/page/4/index.html\",\n \"revision\": \"49a46be3d47f1713600e51265e1130d6\"\n },\n {\n \"url\": \"archives/page/5/index.html\",\n \"revision\": \"6f4d8427c1bf6e83635df6393aa28876\"\n },\n {\n \"url\": \"archives/page/6/index.html\",\n \"revision\": \"e254ace8217dc616b112c82b61651113\"\n },\n {\n \"url\": \"archives/page/7/index.html\",\n \"revision\": \"95bc8752a60ccb8f3309b76381eed4e6\"\n },\n {\n \"url\": \"archives/page/8/index.html\",\n \"revision\": \"ddcad31e4cbf696eb156c4f3aa980062\"\n },\n {\n \"url\": \"archives/page/9/index.html\",\n \"revision\": \"0d77a80308960ba468b27208dfe05dc8\"\n },\n {\n \"url\": \"assets/algolia/algoliasearch.js\",\n \"revision\": \"d5d2500bfe8443b42baaefe4996ee532\"\n },\n {\n \"url\": \"assets/algolia/algoliasearch.min.js\",\n \"revision\": \"9c5e51e57e2b1d888950bf4cb5708c49\"\n },\n {\n \"url\": \"assets/algolia/algoliasearchLite.js\",\n \"revision\": \"ce9b0e62645c036a143f639b92e7789f\"\n },\n {\n \"url\": \"assets/algolia/algoliasearchLite.min.js\",\n \"revision\": \"c2d71f042c879659dbc97f8853b62f21\"\n },\n {\n \"url\": \"categories/Hexo/index.html\",\n \"revision\": \"a808b6727235fca244f85590b3d08721\"\n },\n {\n \"url\": \"categories/index.html\",\n \"revision\": \"9629969647792ce5789f01f308340180\"\n },\n {\n \"url\": \"categories/java/index.html\",\n \"revision\": \"4a44f6d7eca9190a490ffe14a385b478\"\n },\n {\n \"url\": \"categories/java/javaEE/index.html\",\n \"revision\": \"3adef2d1ea6b9dc3cb32712faee63979\"\n },\n {\n \"url\": \"categories/java/javaSE/index.html\",\n \"revision\": \"4eca4f1a42f3ab49e881224a14fb693b\"\n },\n {\n \"url\": \"categories/java/javaSE/page/2/index.html\",\n \"revision\": \"3c61143bbadc0957c40a3311f848b327\"\n },\n {\n \"url\": \"categories/java/javaSE/page/3/index.html\",\n \"revision\": \"9573ccc8ea196a4a035c99a9da3be02e\"\n },\n {\n \"url\": \"categories/java/java面试/index.html\",\n \"revision\": \"fb3e3b068294bc17a5939250fe795703\"\n },\n {\n \"url\": \"categories/java/Maven/index.html\",\n \"revision\": \"7a30cf65bbe46865b922d0f456ca008d\"\n },\n {\n \"url\": \"categories/java/Mybatis/index.html\",\n \"revision\": \"dde00e52a2cddd6eb7af6ef3c4d94678\"\n },\n {\n \"url\": \"categories/java/MySQL/index.html\",\n \"revision\": \"c9814ade489e39cebb1912f95693eb7a\"\n },\n {\n \"url\": \"categories/java/page/2/index.html\",\n \"revision\": \"f7e1596bb44642a4c7091aeda82260d5\"\n },\n {\n \"url\": \"categories/java/page/3/index.html\",\n \"revision\": \"2d0da37ce8771d6fc2d67fb2f86aa733\"\n },\n {\n \"url\": \"categories/java/page/4/index.html\",\n \"revision\": \"7e29e939ad1201b6c8edc5b4071756c0\"\n },\n {\n \"url\": \"categories/java/page/5/index.html\",\n \"revision\": \"f284a2bad3a3f1bb72124deb8551fe8a\"\n },\n {\n \"url\": \"categories/java/page/6/index.html\",\n \"revision\": \"a177180d50d33f141027b436f5656b2c\"\n },\n {\n \"url\": \"categories/java/page/7/index.html\",\n \"revision\": \"eb003ea702cafe6683b36499f100385d\"\n },\n {\n \"url\": \"categories/java/spring/index.html\",\n \"revision\": \"5a0baae7d3c857545e0695e81168a1a2\"\n },\n {\n \"url\": \"categories/java/spring/page/2/index.html\",\n \"revision\": \"49af755ccbf31befcd34dd7217a22153\"\n },\n {\n \"url\": \"categories/java/springMVC/index.html\",\n \"revision\": \"7414094caa7e7b5ec2b7cdc661a0f09f\"\n },\n {\n \"url\": \"categories/win10/index.html\",\n \"revision\": \"b37285587971261050532ccbf19bc133\"\n },\n {\n \"url\": \"categories/杂七杂八/index.html\",\n \"revision\": \"e037f2b0417f1c7b35678a8559b83375\"\n },\n {\n \"url\": \"categories/电磁场/index.html\",\n \"revision\": \"22a57d89abf023e00e1c45b8a668e227\"\n },\n {\n \"url\": \"categories/电磁场/矢量/index.html\",\n \"revision\": \"9ac069b6f4a2086c8506d8f8a1e9a060\"\n },\n {\n \"url\": \"categories/硬件学习/51单片机/index.html\",\n \"revision\": \"fbfb0a8ba40f03ded64038c883c5b4d6\"\n },\n {\n \"url\": \"categories/硬件学习/esp8266/index.html\",\n \"revision\": \"c855e5c8f3e54ab7c2d2d11b91729245\"\n },\n {\n \"url\": \"categories/硬件学习/index.html\",\n \"revision\": \"91afb5c603a77c45b872bd412149b3c6\"\n },\n {\n \"url\": \"categories/硬件学习/page/2/index.html\",\n \"revision\": \"bcf9beb5cb97c6eb445fd69546776a4b\"\n },\n {\n \"url\": \"categories/硬件学习/page/3/index.html\",\n \"revision\": \"6d9c18a25bb08e80a7ea7a3dcb9cac2e\"\n },\n {\n \"url\": \"categories/硬件学习/stm32/index.html\",\n \"revision\": \"2fab0d8466c5db5b41c69deadbca2ef7\"\n },\n {\n \"url\": \"categories/硬件学习/物联网开发/index.html\",\n \"revision\": \"58c9d2b25d41fd982e0067e06fe0a258\"\n },\n {\n \"url\": \"categories/网络安全/index.html\",\n \"revision\": \"37b1818218b426d928d37e57de759ec5\"\n },\n {\n \"url\": \"categories/网络通信/index.html\",\n \"revision\": \"78fa0b8edf8a7ddf0962a1ae109444af\"\n },\n {\n \"url\": \"css/background.css\",\n \"revision\": \"0db0072627f1ecae81e9a4481d7537cf\"\n },\n {\n \"url\": \"css/copyright.css\",\n \"revision\": \"e5126a4122ae5bb36a3d2f56395e71dd\"\n },\n {\n \"url\": \"css/custom.css\",\n \"revision\": \"95dd2781a26bb28e4295306b0754c814\"\n },\n {\n \"url\": \"css/icon.css\",\n \"revision\": \"bef85b48e77ad67185d00ed1afbb68c4\"\n },\n {\n \"url\": \"css/index.css\",\n \"revision\": \"e5cbdaf0abd2d7167d96e1631e1737e5\"\n },\n {\n \"url\": \"css/index.min.css\",\n \"revision\": \"89a7fdf70f3d289198cee53800ad2a7d\"\n },\n {\n \"url\": \"css/mouse.css\",\n \"revision\": \"55e479af44c25819caf5ccfb0d386901\"\n },\n {\n \"url\": \"css/side.css\",\n \"revision\": \"41e18eb432f59d0d8180f15e1629ef29\"\n },\n {\n \"url\": \"css/var.css\",\n \"revision\": \"d41d8cd98f00b204e9800998ecf8427e\"\n },\n {\n \"url\": \"fonts/ajLangMan.ttf\",\n \"revision\": \"37fb83a36178b32a54ac322c274ec3c1\"\n },\n {\n \"url\": \"fonts/JetBrainsMono-Medium.woff2\",\n \"revision\": \"54b6827550ef145b4c1968518a96070f\"\n },\n {\n \"url\": \"fonts/loving.ttf\",\n \"revision\": \"570884f09afc37bc8b94223a828fdf0a\"\n },\n {\n \"url\": \"fonts/shishangblack.ttf\",\n \"revision\": \"896ca9e2117560ea1838537de1e9de07\"\n },\n {\n \"url\": \"fonts/ZhuZiAWan.woff2\",\n \"revision\": \"5d3a54462adf28c19897e7292a33e399\"\n },\n {\n \"url\": \"img/01.jpg\",\n \"revision\": \"b8404ead12c8d52bb294e13d078b71bd\"\n },\n {\n \"url\": \"img/02.jpg\",\n \"revision\": \"d875bf23886eed2f760053a5aa05df27\"\n },\n {\n \"url\": \"img/34.jpg\",\n \"revision\": \"288b5328f34706535f24c4d765b24e32\"\n },\n {\n \"url\": \"img/404.jpg\",\n \"revision\": \"4ef3cfb882b6dd4128da4c8745e9a507\"\n },\n {\n \"url\": \"img/algolia.svg\",\n \"revision\": \"fd40b88ac5370a5353a50b8175c1f367\"\n },\n {\n \"url\": \"img/articles.png\",\n \"revision\": \"fcc028ac9366f8c15741a8d77c866155\"\n },\n {\n \"url\": \"img/articles2.png\",\n \"revision\": \"95ae3b30ec41fb9c8bcdd06f0c294c09\"\n },\n {\n \"url\": \"img/avatar1.jpg\",\n \"revision\": \"6e744e5f778210f988b700018efa34f9\"\n },\n {\n \"url\": \"img/avatar2.jpg\",\n \"revision\": \"30b9763713fa2e81f58fae6f34e40e50\"\n },\n {\n \"url\": \"img/banner.png\",\n \"revision\": \"95124ebf7e6ed51ac65f80e92c25c4b0\"\n },\n {\n \"url\": \"img/blissed01.jpg\",\n \"revision\": \"f57e19c138f825686586f3f9ebae05b4\"\n },\n {\n \"url\": \"img/categories.png\",\n \"revision\": \"85601915a6dec609d7d56a98e8413ea9\"\n },\n {\n \"url\": \"img/favicon.png\",\n \"revision\": \"7a8c47cb5a2149c1a1af21e90ecd9ca7\"\n },\n {\n \"url\": \"img/friend_404.gif\",\n \"revision\": \"68af0be9d22722e74665ef44dd532ba8\"\n },\n {\n \"url\": \"img/huiji.png\",\n \"revision\": \"82d4c602b2f634778630ce5d02a6a4b3\"\n },\n {\n \"url\": \"img/mybatis.png\",\n \"revision\": \"91a2c74cf29863926bd9a6003437cf93\"\n },\n {\n \"url\": \"img/mybatis1.png\",\n \"revision\": \"ca514cda6fb6d3c406d7eb8a3e27ec58\"\n },\n {\n \"url\": \"img/pcbimg.png\",\n \"revision\": \"65d32a5066507f9b57067359cb75c0ad\"\n },\n {\n \"url\": \"img/pcbimg2.png\",\n \"revision\": \"00a107a9e97df662e8121929d11afcf9\"\n },\n {\n \"url\": \"img/plane.png\",\n \"revision\": \"8806f57b1ef84a430207b4223b0c1072\"\n },\n {\n \"url\": \"img/plane1.png\",\n \"revision\": \"f0e88e80119eb50873fed11ce4ba168b\"\n },\n {\n \"url\": \"index.html\",\n \"revision\": \"e71fd0a9ec45e9cdeca6986e14972b8c\"\n },\n {\n \"url\": \"js/main.js\",\n \"revision\": \"01f62452fd05335569c6341d3ac0f52b\"\n },\n {\n \"url\": \"js/search/algolia.js\",\n \"revision\": \"533d980c0d50a0d0d7fe34c41a3e2100\"\n },\n {\n \"url\": \"js/search/local-search.js\",\n \"revision\": \"acb62dcdf7e90930da3f6bf07349fc21\"\n },\n {\n \"url\": \"js/tw_cn.js\",\n \"revision\": \"b3810513e04b13b2d18c6b779c883f85\"\n },\n {\n \"url\": \"js/utils.js\",\n \"revision\": \"12cef07c2e9bc8841a5380df4fd342f5\"\n },\n {\n \"url\": \"link/index.html\",\n \"revision\": \"165b69defbf969aa80c55aaf02b9287a\"\n },\n {\n \"url\": \"live2d-widget/assets/screenshot-1.png\",\n \"revision\": \"30b70e6cd9be9812adcb347536f0da85\"\n },\n {\n \"url\": \"live2d-widget/assets/screenshot-2.png\",\n \"revision\": \"1295844e29a6d6dc3a4aa0db8faa7da7\"\n },\n {\n \"url\": \"live2d-widget/assets/screenshot-3.png\",\n \"revision\": \"4aa1995daf77bc19803648fe6a65c33e\"\n },\n {\n \"url\": \"live2d-widget/autoload.js\",\n \"revision\": \"78870afb2355b97450ea1f93bc8dcad8\"\n },\n {\n \"url\": \"live2d-widget/demo/demo.html\",\n \"revision\": \"2596a8630c0801002b3dff127b50518b\"\n },\n {\n \"url\": \"live2d-widget/demo/login.html\",\n \"revision\": \"6790fe17ee0264f77ba972c941f5d4c3\"\n },\n {\n \"url\": \"live2d-widget/live2d.min.js\",\n \"revision\": \"ee7efff8ff5d1d4bd4a0ff99affd3ec7\"\n },\n {\n \"url\": \"live2d-widget/README.html\",\n \"revision\": \"3d7233f7971913d0ec0f363a14a11cc3\"\n },\n {\n \"url\": \"live2d-widget/waifu-tips.js\",\n \"revision\": \"e01c75f70a9465389471f638b1356bf8\"\n },\n {\n \"url\": \"live2d-widget/waifu.css\",\n \"revision\": \"caedcaf7be20449a974a9eb39e51f259\"\n },\n {\n \"url\": \"music/index.html\",\n \"revision\": \"630d55b1649e3a8728cae81406473cef\"\n },\n {\n \"url\": \"notes/index.html\",\n \"revision\": \"d9fa8fceea30b856762119ac6565c36a\"\n },\n {\n \"url\": \"page/10/index.html\",\n \"revision\": \"1b3c27bb0de5fc0af0af0a3f7d70c9a0\"\n },\n {\n \"url\": \"page/11/index.html\",\n \"revision\": \"8eee013a3809f0399ef8e45c8f2bbb75\"\n },\n {\n \"url\": \"page/2/index.html\",\n \"revision\": \"d89ab174955bfcf24d37602b7e1b1d48\"\n },\n {\n \"url\": \"page/3/index.html\",\n \"revision\": \"e317d50ac8225fe970bda30f0448848e\"\n },\n {\n \"url\": \"page/4/index.html\",\n \"revision\": \"52de6106b999a1dd1b59fc27e284d1f3\"\n },\n {\n \"url\": \"page/5/index.html\",\n \"revision\": \"1a314a8f38f7a84fc9b55f8789b72b5e\"\n },\n {\n \"url\": \"page/6/index.html\",\n \"revision\": \"9df266563df8db5b2e9df3fe7ead35f3\"\n },\n {\n \"url\": \"page/7/index.html\",\n \"revision\": \"9fba2df0fe53ae2f5bd738f1284d8a05\"\n },\n {\n \"url\": \"page/8/index.html\",\n \"revision\": \"44cce65a7a6b3c4f0da2e334e403149d\"\n },\n {\n \"url\": \"page/9/index.html\",\n \"revision\": \"a120e1c07258790b920206d2a45530a5\"\n },\n {\n \"url\": \"poems/index.html\",\n \"revision\": \"4210e51c9ae8f76897dff990a5ce417c\"\n },\n {\n \"url\": \"posts/1294a4bd.html\",\n \"revision\": \"81333aa36cd5165d6851e7e8d6fe503c\"\n },\n {\n \"url\": \"posts/170e1590.html\",\n \"revision\": \"a16954dd2c172396699d1fb277da432b\"\n },\n {\n \"url\": \"posts/17528481.html\",\n \"revision\": \"678cbfce953ba7f81523fa9d318fc1c5\"\n },\n {\n \"url\": \"posts/190b30be.html\",\n \"revision\": \"2e824349660a0b8093114e07eec88454\"\n },\n {\n \"url\": \"posts/1a57c6b6.html\",\n \"revision\": \"b4d82dae1cce2d27269aef19630a00a4\"\n },\n {\n \"url\": \"posts/1a9b2152.html\",\n \"revision\": \"e4442573ec1ee22f0e58dc75822dd8fc\"\n },\n {\n \"url\": \"posts/1b6a7930.html\",\n \"revision\": \"50bdbe737020dde91825222da3d1f88d\"\n },\n {\n \"url\": \"posts/1cd3002f.html\",\n \"revision\": \"b997e719ac7cb9d5c52b00ef684ead58\"\n },\n {\n \"url\": \"posts/1eb33081.html\",\n \"revision\": \"1ac0c21f9815442b445a6f3ea12fcd48\"\n },\n {\n \"url\": \"posts/2080031a.html\",\n \"revision\": \"0bc32d3ef9eca7b676cbea4e694377de\"\n },\n {\n \"url\": \"posts/26c04051.html\",\n \"revision\": \"bdff62ac983eb6da35598d64e5bcbfd0\"\n },\n {\n \"url\": \"posts/27153554.html\",\n \"revision\": \"f9b8b6911de198365061d2cb718032fa\"\n },\n {\n \"url\": \"posts/2a3cf99e.html\",\n \"revision\": \"b43b33474a4b8ddd076ab0a80018a969\"\n },\n {\n \"url\": \"posts/2d7f83ef.html\",\n \"revision\": \"da4485768402f792f25d8096f0bfcddf\"\n },\n {\n \"url\": \"posts/345d8e17.html\",\n \"revision\": \"78ce196f3745403e9a349c02cf5a5c95\"\n },\n {\n \"url\": \"posts/374aa28c.html\",\n \"revision\": \"cf8a4a3ac623119649cfee8fb68ee8a7\"\n },\n {\n \"url\": \"posts/383e39e3.html\",\n \"revision\": \"6200a083d5271eb6721ab84e281d73cf\"\n },\n {\n \"url\": \"posts/38b36403.html\",\n \"revision\": \"b398539bd960298dacaf84bde1d17998\"\n },\n {\n \"url\": \"posts/38b3e585.html\",\n \"revision\": \"1a665f060b1a531fa132909b31fced64\"\n },\n {\n \"url\": \"posts/39aef30f.html\",\n \"revision\": \"a08488bfba824945e69fbdca45e7a64a\"\n },\n {\n \"url\": \"posts/39f00355.html\",\n \"revision\": \"5b252f4d1ea144a1dacc8a6d7c8100c2\"\n },\n {\n \"url\": \"posts/3aac01ca.html\",\n \"revision\": \"6f1b62b3a9ae83a21869d361ef356978\"\n },\n {\n \"url\": \"posts/3afd7950.html\",\n \"revision\": \"969f4e8a18cdebcee45c09a048298e89\"\n },\n {\n \"url\": \"posts/3b32a20d.html\",\n \"revision\": \"d7c205e3801abe40dd2f68cf6060613d\"\n },\n {\n \"url\": \"posts/3de0f4a2.html\",\n \"revision\": \"21b5182fcad8e0d134f19768739948f7\"\n },\n {\n \"url\": \"posts/3f4cddeb.html\",\n \"revision\": \"9cf13103a03369585c298ad7ebc2aeef\"\n },\n {\n \"url\": \"posts/3fb3b16d.html\",\n \"revision\": \"a1c16cee763f593560cc9da0732212f1\"\n },\n {\n \"url\": \"posts/3feecb5e.html\",\n \"revision\": \"92d687ccafaef802800a3f834a184d15\"\n },\n {\n \"url\": \"posts/401491fc.html\",\n \"revision\": \"e49b8431a4e750a44c7361bab4841f4f\"\n },\n {\n \"url\": \"posts/446faab7.html\",\n \"revision\": \"6f1df82f5af9d78d2edd056e53a6199e\"\n },\n {\n \"url\": \"posts/4515cdfc.html\",\n \"revision\": \"6bb68ffeee9ca680205ee0651ff2cab5\"\n },\n {\n \"url\": \"posts/4609d38b.html\",\n \"revision\": \"c16546cad73f1c073a0feda1b3c5770d\"\n },\n {\n \"url\": \"posts/46cd19c8.html\",\n \"revision\": \"b909b086d486c22cfb1be445f8291fb7\"\n },\n {\n \"url\": \"posts/4dc0a7a8.html\",\n \"revision\": \"664927110cdf02f2e5ccc68d130e8429\"\n },\n {\n \"url\": \"posts/4f0e7b50.html\",\n \"revision\": \"fb90e5d3cb4bf700ad504a09d4f2a672\"\n },\n {\n \"url\": \"posts/51a2ab89.html\",\n \"revision\": \"2950dbc224cf5132c9b94d8865688e01\"\n },\n {\n \"url\": \"posts/52b44ec7.html\",\n \"revision\": \"53b5850a69cc0949f4b18101998714ee\"\n },\n {\n \"url\": \"posts/52da8eea.html\",\n \"revision\": \"4da011e6fcfd8608c135a32014500856\"\n },\n {\n \"url\": \"posts/52e2375.html\",\n \"revision\": \"e1f6d3ab2d0c3a47a3c60b0d017f019d\"\n },\n {\n \"url\": \"posts/530cfffd.html\",\n \"revision\": \"af4e2dc316ffd91a7c1ba60d35058af4\"\n },\n {\n \"url\": \"posts/54667693.html\",\n \"revision\": \"f7776dd9f459df57c11a1a8ec791610d\"\n },\n {\n \"url\": \"posts/54a4218.html\",\n \"revision\": \"92aea03b1cd72fcc2d4c9dd0a1ffdb76\"\n },\n {\n \"url\": \"posts/57ac54fe.html\",\n \"revision\": \"716b6dca031440e2dc4cd2c839f83219\"\n },\n {\n \"url\": \"posts/59e405f3.html\",\n \"revision\": \"8d8e56c187d9dd4a88cb60be40d50383\"\n },\n {\n \"url\": \"posts/5dd9c9e3.html\",\n \"revision\": \"4e6e74efed1d8538479c030d24e01a9f\"\n },\n {\n \"url\": \"posts/5e64b19a.html\",\n \"revision\": \"818a275d5b85d6a3c0f91fba1895b171\"\n },\n {\n \"url\": \"posts/63036c3c.html\",\n \"revision\": \"270d87c87ec356e692d2036171683add\"\n },\n {\n \"url\": \"posts/683d1a78.html\",\n \"revision\": \"cddeb331870512451d1c21b71ac9ee03\"\n },\n {\n \"url\": \"posts/68f2c599.html\",\n \"revision\": \"081a44a5e7cab270e72b839ec2d58c47\"\n },\n {\n \"url\": \"posts/6e81c43e.html\",\n \"revision\": \"ba35351b9ba3b2aaecf90068700d75f3\"\n },\n {\n \"url\": \"posts/73ba8569.html\",\n \"revision\": \"010429a34d4ed5e1ee5b2b154e3f3fe9\"\n },\n {\n \"url\": \"posts/752d4bdb.html\",\n \"revision\": \"de7f8ef1210aa5684ab48f528010b21f\"\n },\n {\n \"url\": \"posts/753dfa3e.html\",\n \"revision\": \"887364f41e068a2f0f96dc7c520b66b9\"\n },\n {\n \"url\": \"posts/78903d87.html\",\n \"revision\": \"414f9ca5c71201e489b69efe9f0709c3\"\n },\n {\n \"url\": \"posts/79c0e989.html\",\n \"revision\": \"f67181c4b387b3b15caaac430c9e273a\"\n },\n {\n \"url\": \"posts/7dc1e032.html\",\n \"revision\": \"36258e645dfc75aa0145ce8bce45e727\"\n },\n {\n \"url\": \"posts/7f2244ba.html\",\n \"revision\": \"63bc2cbe9badee1cf2e7e81e51710c7e\"\n },\n {\n \"url\": \"posts/81ecdf4a.html\",\n \"revision\": \"e35dfc6f48553503ed0d47db3df74c08\"\n },\n {\n \"url\": \"posts/88ad757.html\",\n \"revision\": \"dddf77dc281e66b3e07096b09254fe26\"\n },\n {\n \"url\": \"posts/890c8bdb.html\",\n \"revision\": \"92d1bd1a8ae7ce9b65708fe32e2b4170\"\n },\n {\n \"url\": \"posts/894e18ee.html\",\n \"revision\": \"80c8ca83819c5e0fe7733e6c29c049de\"\n },\n {\n \"url\": \"posts/89cf4bca.html\",\n \"revision\": \"86850687a5013204529d6a50155e1467\"\n },\n {\n \"url\": \"posts/8a45e6a3.html\",\n \"revision\": \"d3b34d8a029c03b265f52cd93a59f6ec\"\n },\n {\n \"url\": \"posts/8b75069d.html\",\n \"revision\": \"865310c8cc59577a31e1f4e892bd0ad4\"\n },\n {\n \"url\": \"posts/94566522.html\",\n \"revision\": \"ef99600a1d5400206f02592a2cf6cdef\"\n },\n {\n \"url\": \"posts/9dc8e4af.html\",\n \"revision\": \"ae00041a27fd9df8b1091e54c5987c83\"\n },\n {\n \"url\": \"posts/9e0a8624.html\",\n \"revision\": \"97a0b205072e4d7507115fab14a877fb\"\n },\n {\n \"url\": \"posts/a1a1eb5.html\",\n \"revision\": \"522417ac47ead0748a6dc7133e2a979b\"\n },\n {\n \"url\": \"posts/a4682555.html\",\n \"revision\": \"2501e158602845b9e8837ce45b02debe\"\n },\n {\n \"url\": \"posts/a4c109bd.html\",\n \"revision\": \"7906b2c2ae9e76bcb31ba2f29483b103\"\n },\n {\n \"url\": \"posts/ab09b476.html\",\n \"revision\": \"c6175b8f81aab232f77e50ea2deded95\"\n },\n {\n \"url\": \"posts/ab69c7ec.html\",\n \"revision\": \"0e48992feee907264ea828f04bb599ee\"\n },\n {\n \"url\": \"posts/adfcabaa.html\",\n \"revision\": \"f9b605b4084730de310182ac4c2ab766\"\n },\n {\n \"url\": \"posts/af3c95f0.html\",\n \"revision\": \"2a3f2046efc354662f15abed84e7667e\"\n },\n {\n \"url\": \"posts/b3cec184.html\",\n \"revision\": \"dd30ff1b7e08376b003ab828f51fbf65\"\n },\n {\n \"url\": \"posts/b54f36d8.html\",\n \"revision\": \"9b14bff1930d86c722b618871576a216\"\n },\n {\n \"url\": \"posts/b836e65.html\",\n \"revision\": \"25fb2b122cce636c35b3e273219e7e74\"\n },\n {\n \"url\": \"posts/b8426075.html\",\n \"revision\": \"3cfe512860fbdbd59b77bee85d5b2534\"\n },\n {\n \"url\": \"posts/b9bb0bd5.html\",\n \"revision\": \"bd164afaaa887922d7c9eda27fc016b6\"\n },\n {\n \"url\": \"posts/ba43a8dc.html\",\n \"revision\": \"bc0528f3680e54b6345fb472b92145db\"\n },\n {\n \"url\": \"posts/baad5185.html\",\n \"revision\": \"0e1e366409a5bf5419d9df1e1756b22d\"\n },\n {\n \"url\": \"posts/bdfc8ea1.html\",\n \"revision\": \"eab73914239cbb16d479578c59e1c30e\"\n },\n {\n \"url\": \"posts/be39d4e8.html\",\n \"revision\": \"5d86128c08d2d0745c89929ef5fcc6ff\"\n },\n {\n \"url\": \"posts/c0626013.html\",\n \"revision\": \"4a3846afe8928b58580c8d84d77df401\"\n },\n {\n \"url\": \"posts/c25015c5.html\",\n \"revision\": \"75c735dda5702355ac81ac3682b7cc9a\"\n },\n {\n \"url\": \"posts/c27dd6ec.html\",\n \"revision\": \"635767f606154f5de863b9e1aed2f4ac\"\n },\n {\n \"url\": \"posts/c2d1d169.html\",\n \"revision\": \"e77d3649769f351d4ae8003d68e00ea3\"\n },\n {\n \"url\": \"posts/c4d68191.html\",\n \"revision\": \"aa7d0142581a5b69c52df299310639f9\"\n },\n {\n \"url\": \"posts/c5d59ae.html\",\n \"revision\": \"7a6e7e12c36daea4eefc748ca4cb4f6a\"\n },\n {\n \"url\": \"posts/c63fec2a.html\",\n \"revision\": \"92a841638671c97ac5d6d9dcf9d5e01b\"\n },\n {\n \"url\": \"posts/c8eb4af9.html\",\n \"revision\": \"c868f029ca0c67f8835954a3e1fc6d5a\"\n },\n {\n \"url\": \"posts/c9f91dab.html\",\n \"revision\": \"c408b250c599bf1e28b119a1a49a2a7c\"\n },\n {\n \"url\": \"posts/d150c3f1.html\",\n \"revision\": \"44d484b7f5066c9a335e6ec3bb89d389\"\n },\n {\n \"url\": \"posts/d29e4d17.html\",\n \"revision\": \"c269f6cbec540242834d5724b2251cf4\"\n },\n {\n \"url\": \"posts/d709dda.html\",\n \"revision\": \"c03d77a3ab538cef7c4a9a0c81597942\"\n },\n {\n \"url\": \"posts/d8642fb9.html\",\n \"revision\": \"1858617b76b9729b1a3f45f3d4223a9f\"\n },\n {\n \"url\": \"posts/d959138b.html\",\n \"revision\": \"86ea256c08936582f6d2d0b57d1b0c00\"\n },\n {\n \"url\": \"posts/dc7f8ae1.html\",\n \"revision\": \"58a97cec2c52b339dd2f0bd592b08f3f\"\n },\n {\n \"url\": \"posts/e0bbd322.html\",\n \"revision\": \"32fa8fc14c5cf2e9de3b0ad408ff00dc\"\n },\n {\n \"url\": \"posts/e7ba7594.html\",\n \"revision\": \"6bf2be8d9c809f0de312de6a1fa663aa\"\n },\n {\n \"url\": \"posts/ecec7d3f.html\",\n \"revision\": \"8eade11b113ff3409e2eaaf6e09301d5\"\n },\n {\n \"url\": \"posts/ee4b2b4d.html\",\n \"revision\": \"e6d1ae3ee12ee60fb04af277fc4a7ab0\"\n },\n {\n \"url\": \"posts/f218ca42.html\",\n \"revision\": \"a84d122a809618d143a9a8f4b3eaa443\"\n },\n {\n \"url\": \"posts/f5c95c.html\",\n \"revision\": \"f12e497f0aa22e4f7fd5e188af1b5be8\"\n },\n {\n \"url\": \"posts/f68104ec.html\",\n \"revision\": \"c31a7e027b3b44fabf03981d50a61fab\"\n },\n {\n \"url\": \"posts/f8dae6c5.html\",\n \"revision\": \"c7666db6597bb22209c139e9640bb88e\"\n },\n {\n \"url\": \"tags/51单片机/index.html\",\n \"revision\": \"f08e8b5ffe0624ddfaa775b6d4b21cba\"\n },\n {\n \"url\": \"tags/esp8266/index.html\",\n \"revision\": \"8b3b3f3c8b6d0e536a7cfb9a6248e4ef\"\n },\n {\n \"url\": \"tags/esp8266/page/2/index.html\",\n \"revision\": \"2bb02c968eb52ff4904a4e2cd5683c5e\"\n },\n {\n \"url\": \"tags/Hexo/index.html\",\n \"revision\": \"5bd55b99cb98c4c5ddc51786dbe669a7\"\n },\n {\n \"url\": \"tags/Http/index.html\",\n \"revision\": \"3753cd765a9b96a6d042da45da703b5a\"\n },\n {\n \"url\": \"tags/index.html\",\n \"revision\": \"dcec168a6e990064d105694b27e428d9\"\n },\n {\n \"url\": \"tags/javaSE/index.html\",\n \"revision\": \"18e46ba4c1c114d36aa599b8542c158b\"\n },\n {\n \"url\": \"tags/javaSE/page/2/index.html\",\n \"revision\": \"01a38fdaf0ded022d440c810ffb2c627\"\n },\n {\n \"url\": \"tags/javaSE/page/3/index.html\",\n \"revision\": \"41722f9b5d82fd33bf350e85e45d4565\"\n },\n {\n \"url\": \"tags/Java面试/index.html\",\n \"revision\": \"44727da649352de86dec2661d9d7f979\"\n },\n {\n \"url\": \"tags/Maven/index.html\",\n \"revision\": \"436777f83ac11b3f2a5d05392d5f1e19\"\n },\n {\n \"url\": \"tags/MDK-keil5/index.html\",\n \"revision\": \"32cf6e202f1221ccd47ff86b6c73f6ef\"\n },\n {\n \"url\": \"tags/Mybatis/index.html\",\n \"revision\": \"9e8a4aa0292b81a86d45b71f2355817e\"\n },\n {\n \"url\": \"tags/MySQL/index.html\",\n \"revision\": \"17db5afc6d504a888120538274ddca26\"\n },\n {\n \"url\": \"tags/Servlet/index.html\",\n \"revision\": \"b69131c5be9ba12355cfe5f17e7e20cf\"\n },\n {\n \"url\": \"tags/spring/index.html\",\n \"revision\": \"7f9c5cd25a795326b801cc7c3b966e9c\"\n },\n {\n \"url\": \"tags/spring/page/2/index.html\",\n \"revision\": \"909617a5c64e4196e8262be893f0a40f\"\n },\n {\n \"url\": \"tags/springMVC/index.html\",\n \"revision\": \"74f24b15fb21549e8bbac15db772fa78\"\n },\n {\n \"url\": \"tags/stm32/index.html\",\n \"revision\": \"cd05b8ad3ca71fd8681aeb888a2f3f10\"\n },\n {\n \"url\": \"tags/Tomcat/index.html\",\n \"revision\": \"c35de9c4fcac9aa3133d0cfdd38ec717\"\n },\n {\n \"url\": \"tags/win10/index.html\",\n \"revision\": \"4c7dfbe269f0040fc0ab162af8f6e58b\"\n },\n {\n \"url\": \"tags/图床/index.html\",\n \"revision\": \"c03fcc5f4f14ac8b7dd942498dd54efd\"\n },\n {\n \"url\": \"tags/物联网/index.html\",\n \"revision\": \"1b9368d355eb19851e83f73e1d975c7d\"\n },\n {\n \"url\": \"tags/矢量/index.html\",\n \"revision\": \"a8657e2fe91b20c04cc43cc80021b35c\"\n },\n {\n \"url\": \"tags/网络安全/index.html\",\n \"revision\": \"3bfea466ec8b0380de196387d6f25801\"\n },\n {\n \"url\": \"tags/网络通信/index.html\",\n \"revision\": \"734785869d51c2eadb6d23fab19f5458\"\n },\n {\n \"url\": \"web-links/index.html\",\n \"revision\": \"fe36137d3417b0550f1737f1014eadd4\"\n }\n], {});\n\n\n\n\nworkbox_routing_registerRoute(/^https:\\/\\/cdn\\.example\\.com\\/.*/, new workbox_strategies_CacheFirst(), 'GET');\n\n\n\n\n"],"names":["self","skipWaiting","workbox_strategies_CacheFirst"],"mappings":"szBAsBAA,KAAKC,kDAU+B,CAClC,KACS,oBACK,oCAEd,KACS,4BACK,oCAEd,KACS,uCACK,oCAEd,KACS,8CACK,oCAEd,KACS,8CACK,oCAEd,KACS,uCACK,oCAEd,KACS,uCACK,oCAEd,KACS,uCACK,oCAEd,KACS,uCACK,oCAEd,KACS,8CACK,oCAEd,KACS,uCACK,oCAEd,KACS,8CACK,oCAEd,KACS,8CACK,oCAEd,KACS,uCACK,oCAEd,KACS,8CACK,oCAEd,KACS,oCACK,oCAEd,KACS,4CACK,oCAEd,KACS,4CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,2CACK,oCAEd,KACS,+BACK,oCAEd,KACS,uCACK,oCAEd,KACS,uCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,2CACK,oCAEd,KACS,+CACK,oCAEd,KACS,+CACK,oCAEd,KACS,mDACK,oCAEd,KACS,sCACK,oCAEd,KACS,iCACK,oCAEd,KACS,sCACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,oDACK,oCAEd,KACS,oDACK,oCAEd,KACS,6CACK,oCAEd,KACS,4CACK,oCAEd,KACS,8CACK,oCAEd,KACS,4CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,oDACK,oCAEd,KACS,gDACK,oCAEd,KACS,uCACK,oCAEd,KACS,sCACK,oCAEd,KACS,qCACK,oCAEd,KACS,wCACK,oCAEd,KACS,4CACK,oCAEd,KACS,8CACK,oCAEd,KACS,sCACK,oCAEd,KACS,6CACK,oCAEd,KACS,6CACK,oCAEd,KACS,4CACK,oCAEd,KACS,4CACK,oCAEd,KACS,sCACK,oCAEd,KACS,sCACK,oCAEd,KACS,8BACK,oCAEd,KACS,6BACK,oCAEd,KACS,0BACK,oCAEd,KACS,wBACK,oCAEd,KACS,yBACK,oCAEd,KACS,6BACK,oCAEd,KACS,yBACK,oCAEd,KACS,wBACK,oCAEd,KACS,uBACK,oCAEd,KACS,+BACK,oCAEd,KACS,4CACK,oCAEd,KACS,4BACK,oCAEd,KACS,mCACK,oCAEd,KACS,iCACK,oCAEd,KACS,sBACK,oCAEd,KACS,sBACK,oCAEd,KACS,sBACK,oCAEd,KACS,uBACK,oCAEd,KACS,2BACK,oCAEd,KACS,4BACK,oCAEd,KACS,6BACK,oCAEd,KACS,2BACK,oCAEd,KACS,2BACK,oCAEd,KACS,0BACK,oCAEd,KACS,6BACK,oCAEd,KACS,8BACK,oCAEd,KACS,2BACK,oCAEd,KACS,8BACK,oCAEd,KACS,yBACK,oCAEd,KACS,2BACK,oCAEd,KACS,4BACK,oCAEd,KACS,0BACK,oCAEd,KACS,2BACK,oCAEd,KACS,yBACK,oCAEd,KACS,0BACK,oCAEd,KACS,sBACK,oCAEd,KACS,sBACK,oCAEd,KACS,gCACK,oCAEd,KACS,qCACK,oCAEd,KACS,uBACK,oCAEd,KACS,uBACK,oCAEd,KACS,2BACK,oCAEd,KACS,iDACK,oCAEd,KACS,iDACK,oCAEd,KACS,iDACK,oCAEd,KACS,qCACK,oCAEd,KACS,wCACK,oCAEd,KACS,yCACK,oCAEd,KACS,uCACK,oCAEd,KACS,qCACK,oCAEd,KACS,uCACK,oCAEd,KACS,mCACK,oCAEd,KACS,4BACK,oCAEd,KACS,4BACK,oCAEd,KACS,8BACK,oCAEd,KACS,8BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,6BACK,oCAEd,KACS,4BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,6BACK,oCAEd,KACS,+BACK,oCAEd,KACS,+BACK,oCAEd,KACS,iCACK,oCAEd,KACS,mCACK,oCAEd,KACS,0CACK,oCAEd,KACS,gCACK,oCAEd,KACS,gCACK,oCAEd,KACS,2BACK,oCAEd,KACS,kCACK,oCAEd,KACS,yCACK,oCAEd,KACS,yCACK,oCAEd,KACS,kCACK,oCAEd,KACS,iCACK,oCAEd,KACS,qCACK,oCAEd,KACS,mCACK,oCAEd,KACS,iCACK,oCAEd,KACS,mCACK,oCAEd,KACS,kCACK,oCAEd,KACS,yCACK,oCAEd,KACS,qCACK,oCAEd,KACS,iCACK,oCAEd,KACS,kCACK,oCAEd,KACS,iCACK,oCAEd,KACS,8BACK,oCAEd,KACS,+BACK,oCAEd,KACS,8BACK,oCAEd,KACS,gCACK,oCAEd,KACS,gCACK,oCAEd,KACS,gCACK,qCAEb,oBAK2B,mCAAoC,IAAIC,aAAiC"} \ No newline at end of file diff --git "a/source/_posts/\347\256\227\346\263\225/\350\264\250\346\225\260.md" "b/source/_posts/\347\256\227\346\263\225/\350\264\250\346\225\260.md" new file mode 100644 index 00000000..47f7ed86 --- /dev/null +++ "b/source/_posts/\347\256\227\346\263\225/\350\264\250\346\225\260.md" @@ -0,0 +1,38 @@ +--- +title: 质数 +description: 关于求质数的基本方法,如何判断质数、筛选质数 +tags: + - 质数 +categories: + - 算法 + - 数学知识 +abbrlink: 4c75c6b3 +date: 2021-09-17 10:18:12 +cover: https://gitee.com/ajream/images/raw/master/img/20210917104053_algorithm2.png +--- + +## 质数 + +### 试除法判定质数 + +代码模板 + +```c +bool is_prime(int x){ + if(x<2) return false; + for(int i=2; i<= x / i; ++i){ + if(x % i == 0) + return false; + } + + return true; +} +``` + +### 试除法分解质因数 + + +### 朴素筛选法筛选质数 + + +### 线性筛选法筛选质数 \ No newline at end of file -- GitLab