-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 297 KB
/
content.json
1
{"meta":{"title":"zhiheng's blog","subtitle":"喜于分享,勤于积累;欢迎关注我的微信公众号:治恒说说","description":"","author":"Shure","url":"https://dddreams.github.io"},"pages":[{"title":"","date":"2021-11-07T14:42:57.105Z","updated":"2021-11-07T14:42:57.105Z","comments":false,"path":"about/index.html","permalink":"https://dddreams.github.io/about/index.html","excerpt":"","text":"个人资料 Email:[email protected] Github:@dddreams CSDN:@dddreams 微博:@dddreams QQ: 289750872 微信: DShure 个人博客:托管至 Github zhiheng’s blog 微信公众号:治恒说说(zhihengsBlog) 工作经历 2015-10 至今甘肃兰州某公司,想了解或入坑,请微信联系! 2014-12~2015-04北京健康在线技术开发有限公司 简介 看到上面的资料,想必已经猜到了,我是一名猿,不错,地地道道的科班出身然后沦落为一名搬砖的猿,毕业于一所不知名的二本院校,是一个比较爱码字的人,早在学校的时候,就在新浪,网易,博客园等论坛网站上有身影,只不过那时的修为不够,写一些文章或者技术博客都是在瞎逼逼,到后来也就逐渐不去维护了。 直到毕业以后,刚进公司,领导不给安排工作,安排的也就是熟悉公司制度啊文化啊这些的,所以又拿起了这个爱码字的爱好,同时也胡乱折腾了不少,当时的个人博客还不是基于 github 的,是在新浪的 SEA 上申请了云服务器,个人博客很轻量级的,因此选择了 PHP 的服务器,也是因为便宜。PHP 对我来说是陌生的,要从头开始学,开始折腾,不过也还可以,折腾到最后也是有模有样的,但是后来发现现在的好多个人博客都做的很不错,页面炫酷,使用了动画,看着很6的样子,细细钻研之后,发现都用的是现成的模版,比如 hexo ,jekyll,WordPress等结合 github pages 搭建的博客,所以我也折腾着搭起了自己的博客,长得就现在这个样子 zhiheng’s blog( https://dddreams.github.io/ ) 这个公众号(zhiheng博客)也是在刚工作的时候注册的,之前提起过,刚开始是想做一个问答型的公众号,由于这种需要有后台的支持,而且要有数据库等,不适合个人运营,所以之前的想法就没能实现,现在只是基于微信后台来写一些博客心得而已。 我很喜欢现在的工作,喜欢用代码实现一切,喜欢思考,喜欢飘扬在思维高速运转的天空,他让我有一种统领全局,一切尽在掌握之中的感觉,不信你来试试,哈哈。 另外这个公众号(zhiheng博客)和博客zhiheng’s blog( https://dddreams.github.io/ )仅属于我个人,一些观点都代表个人,跟任何组织与机构无关。 其实我也是个普通人,是一个努力为生活打拼的青年,我将继续坚持写作,继续为未来美好生活而奋斗,相信努力总会得到回报。 完毕。 更多文章请关注微信公众号: 治恒说说"},{"title":"","date":"2021-11-25T00:57:44.324Z","updated":"2021-11-25T00:57:44.324Z","comments":false,"path":"demo/index.html","permalink":"https://dddreams.github.io/demo/index.html","excerpt":"","text":"有趣好玩的前端代码html2canvas利用 html2canvas ,生成 “我们是谁” 的图片(使用手机查看,效果更好) zhiheng聊天zhiheng 聊天 zhiheng 聊天,简易的聊天程序,不过程序使用英语跟你聊天,是你学习英语的好帮手!来吧,开始你的英语学习之旅。 程序员的小浪漫程序员的小浪漫 都说程序员呆傻、不善言语,但是浪漫起来也是别人无法比拟的,点击上面的链接,查看程序员的小浪漫。 项目来源:https://github.com/NewNewKing/SmallRomance 今儿就想发个红包~~~想要红包吗???使劲戳~今儿就想发个红包,戳吧!!! 亲爱的,嫁给我好吗?点点看有惊喜哦~ 浪漫求婚,点点看~~~ 项目地址:https://github.com/sunweiling/wedding 恋爱时光亲爱的,这是我们相爱在一起的时光。 运动倒计时运动倒计时,Readying GO!!! 生日蛋糕,送给你心爱的她生日蛋糕,送给你心爱的她浪漫花瓣雨项目地址:https://github.com/QuarkGluonPlasma/threejs-exercize 持续更新中 . . . 更多文章请关注微信公众号: 治恒说说"},{"title":"","date":"2020-12-22T04:49:39.086Z","updated":"2017-04-07T09:47:22.621Z","comments":true,"path":"tags/index.html","permalink":"https://dddreams.github.io/tags/index.html","excerpt":"","text":""}],"posts":[{"title":"github精选-github加速访问神器","slug":"211206-github精选-github加速访问神器","date":"2021-12-06T05:12:49.000Z","updated":"2021-12-06T06:10:14.799Z","comments":true,"path":"211206-github精选-github加速访问神器.html","link":"","permalink":"https://dddreams.github.io/211206-github精选-github加速访问神器.html","excerpt":"","text":"最近分享了很多 Github 的项目或工具后,有很多同学反映 Github 就是一个神器的网站,要么打不开,要么打开了速度也慢的要命,文件下载到一半网络断了,气的人没脾气。还有更奇怪的是有些公司尽然把 Github 给禁了,他还是个软件公司。好吧,有这样烦恼的同学看过来,今天推荐一个 Github 加速神器FastGithub。 它主要解决 GitHub 打不开、用户头像无法加载、releases 无法上传下载、git-clone、git-pull、git-push 失败等问题。他的原理就是拿到 Github 大量的 IP 数据,来检测哪些 IP 可用,哪些 IP 访问速度最佳,然后进行解析,修改本机的dns服务指向 FastGithub 自身,解析匹配的域名为 FastGithub 自身的 IP,请求不受污染的dns服务(dnscrypt-proxy)获取域名的IP 选择最优的 IP 进行 ssh 或 https 反向代理。 软件可以傻瓜式使用,不用任何设置,只需要双击运行即可,与代理工具、vpn等不冲突(因为不是同一种类型),也不修改你的系统网络配置。下面是软件目录和 UI 界面。 并且跨平台,支持Windows、Linux、Mac平台,根据自己的环境下载相应安装包。 开源地址:https://github.com/dotnetcore/FastGithub 下载地址:https://github.com/dotnetcore/FastGithub/releases 有人说,这是一个死循环,访问不了 Github,如何获取到 FastGithub? 好的,奉上国内下载地址:https://gitee.com/jiulang/fast-github。gitee 上不是最新版本,下载后再去 Github 下载最新版本即可。 更多文章请关注微信公众号: 治恒说说","categories":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/categories/Github/"}],"tags":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/tags/Github/"},{"name":"软件","slug":"软件","permalink":"https://dddreams.github.io/tags/软件/"}],"keywords":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/categories/Github/"}]},{"title":"github精选-一款高颜值的Redis客户端","slug":"211204-github精选-一款高颜值的Redis客户端","date":"2021-12-04T14:31:06.000Z","updated":"2021-12-06T01:27:41.216Z","comments":true,"path":"211204-github精选-一款高颜值的Redis客户端.html","link":"","permalink":"https://dddreams.github.io/211204-github精选-一款高颜值的Redis客户端.html","excerpt":"","text":"作为开发者,Redis大家都不陌生,一个开源的、高性能的、基于内存运行的键值对数据库,常用来做缓存,Session共享服务器等,进大厂面试必问的内容,公众号后台回复Redis领取关于 Redis 的面试资料。 好了言归正传,既然他是个数据库,那么我们在使用过程中就会和关系型数据库一样,要进行增删改查这些操作,可惜的是 Redis 官方没有提供可视化工具,便于我们管理 Redis,要对他进行操作就只能这样: 1234567891011121314151617181920cd /sur/local/redis./redis-cli -h 127.0.0.1 -p 6379127.0.0.1:6379> auth 123456 # 如果设置密码127.0.0.1:6379> # 查看所有的 key127.0.0.1:6379> keys *# 新增键值对127.0.0.1:6379> set test 'shure'OK# 查看某个 key 的值127.0.0.1:6379> get test\"shure\"# 删除键值对127.0.0.1:6379> del test(integer) 1# 清空单个库的数据127.0.0.1:6379> select 0127.0.0.1:6379> flushdb# 清空整个库的数据127.0.0.1:6379> flushall 或者有时候编写脚本操作一些常用的数据,可以直接使用命令: 123456redis-cli -h 127.0.0.1 -p 6379 -a 123456 keys \"login:*\" |xargs redis-cli -h 127.0.0.1 -p 6379 -a 123456 del## -a 密码-n 指定数据库-h ip地址-p 端口 命令操作起来的确很装逼,但是很繁琐,而且很容易忘记,所有就涌现出了很多第三方的客户端工具,常用的有Redis Desktop Manager、medis、RedisPlus、FastoRedis、Red(Mac上常用的工具)Redis Insight、Iedis2(Idea 插件)等等,而今天我要推荐这款高颜值的Redis客户端工具AnotherRedisDesktopManager。 先来看看他的样子 有黑暗主题、可切换简体中文、新建连接支持SSH跳板机、SSH模式、集群模式、可以查看Redis状态、增删改操作方便直观,并且可以修改过期时间,数据类型,当然也可以一键切换至命令行模式,更是跨平台可以运行在Windows、Linux、Mac平台,就算加载大数据量也不会卡死。 就是这样一款高颜值,又功能强大的客户端工具,你不赶快试试? 开源地址:https://github.com/qishibo/AnotherRedisDesktopManager 下载地址:https://github.com/qishibo/AnotherRedisDesktopManager/releases 国内下载:https://gitee.com/qishibo/AnotherRedisDesktopManager/releases 更多文章请关注微信公众号: 治恒说说","categories":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/categories/Github/"}],"tags":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/tags/Github/"},{"name":"软件","slug":"软件","permalink":"https://dddreams.github.io/tags/软件/"}],"keywords":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/categories/Github/"}]},{"title":"Python读取Excel中的图片(二)","slug":"211203-Python读取Excel中的图片(二)","date":"2021-12-03T06:36:51.000Z","updated":"2021-12-03T07:15:24.913Z","comments":true,"path":"211203-Python读取Excel中的图片(二).html","link":"","permalink":"https://dddreams.github.io/211203-Python读取Excel中的图片(二).html","excerpt":"","text":"继上一篇 使用Python读取Excel中的图片并对应到记录 ,经过实践之后,又发现了问题,便有了今天这篇。 1、经过实践后发现的问题代码经过实践后,发现还是有问题,有些图片还是对应不到相应的记录,于是又开始了一波debugger,发现不是代码的锅,而是Excel解压后drawing1.xml的锅,来看看我们解析xml的代码: 1234567def _f(subElementObj): for anchor in subElementObj: xdr_from = anchor.getElementsByTagName('xdr:from')[0] col = xdr_from.childNodes[0].firstChild.data # 获取标签间的数据 row = xdr_from.childNodes[2].firstChild.data embed = anchor.getElementsByTagName('xdr:pic')[0].getElementsByTagName('xdr:blipFill')[0].getElementsByTagName('a:blip')[0].getAttribute('r:embed') # 获取属性 image_info[(int(row), int(col))] = img_dict.get(int(embed.replace('rId', '')), {}).get(img_feature) 要解析的xml文档部分内容: 12345678<xdr:pic> <xdr:blipFill> ... <a:blip r:embed=\"rId1\" cstate=\"print\"> ... </a:blip> </xdr:blipFill></xdr:pic> 获取到<a:blip>元素的r:embed属性,即对应团片的序号,实际上,如果Excel内容是从其他地方复制过来的,他的序号与图片的序号对应不上,导致的问题,遗憾的是没找到什么原因,不知道Excel中是如何对应的,有兴趣的同学可以研究下。 2、另一种方式的实现另一种方式是使用openpyxl和openpyxl_image_loader库,按行读取,loader 图片进行保存,完整代码见:new_read_data.py。 3、新增的需求 循环读取在某个目录下的多个文件)) 123for root, dirs, files in os.walk(source_root): for file in files: print(os.path.join(root, file)) leader 要求照片大于200K不入库,于是添加了压缩图片的功能,我将压缩图片的代码分离了出来compress_image.py。 将有问题的数据记录下来,写入 Excel,于是有了写入Excel的代码。 123456789wb = Workbook()ws = wb.create_sheet(\"存在问题的数据\", 0)index = 1for i in range(len(error_data)): index = index + 1 arr_list = error_data[i].split(\"|\") for j in range(len(arr_list)): ws.cell(row = index, column= j+1, value = arr_list[j])wb.save(target_root + '存在问题的数据.xlsx') 照片使用电话号码命名,并生成日志,写入文件。 4、存在的问题由于原始数据中存在照片未采集的记录,但是提取到的数据中这些记录都有对应的照片,原来image_loader = SheetImageLoader(ws)每次读完不会清空字典,所以就会把上一个文件中对应行的照片读取到当前文件的这一行,经过搜索查找发现是openpyxl-image-loader的问题,相关issues地址:images should not be static variable of SheetImageLoader 。所以在每次循环结束将image_loader 清空即可,添加这行代码: 1image_loader._images.clear() 5、通过VB导出图片其实提取Excel中的图片可以使用VB实现,直接在Excel的sheet上右键【查看代码】然后粘贴一下代码执行就会将图片导出来,并且能以任一列的值命名。 1234567891011121314151617Sub 导出图片() On Error Resume Next MkDir ThisWorkbook.Path & \"\\图片\" For Each pic In ActiveSheet.Shapes If pic.Type = 13 Then RN = pic.TopLeftCell.Offset(0, -3).Value pic.Copy With ActiveSheet.ChartObjects.Add(0, 0, pic.Width, pic.Height).Chart '创建图片 .Parent.Select .Paste .Export ThisWorkbook.Path & \"\\图片\\\" & RN & \".jpg\" .Parent.Delete End With End If Next MsgBox \"导出图片完成! \"End Sub 6、总结经过不断的折腾,发现条条大路通罗马才是真理,不管你用什么方式实现,发现问题、解决问题才是最重要的经历。 更多文章请关注微信公众号: 治恒说说","categories":[{"name":"Python","slug":"Python","permalink":"https://dddreams.github.io/categories/Python/"}],"tags":[{"name":"Python","slug":"Python","permalink":"https://dddreams.github.io/tags/Python/"},{"name":"Excel","slug":"Excel","permalink":"https://dddreams.github.io/tags/Excel/"}],"keywords":[{"name":"Python","slug":"Python","permalink":"https://dddreams.github.io/categories/Python/"}]},{"title":"github精选-Linux命令大全","slug":"211124-github精选-Linux命令大全","date":"2021-11-24T07:31:41.000Z","updated":"2021-11-24T09:10:24.554Z","comments":true,"path":"211124-github精选-Linux命令大全.html","link":"","permalink":"https://dddreams.github.io/211124-github精选-Linux命令大全.html","excerpt":"","text":"对于程序员,和 Linux 打交道那是一项必不可少的技能,有很多同学问我,Linux命令这么多,该怎么从入门到精通呢?今天给大家推荐一个学习和查询Linux命令的好工具linux-command. 先来看看效果 该仓库搜集了570多个Linux命令,内容包含 Linux 命令手册、详解、学习等,非常值得收藏的 Linux 命令速查手册。并且作者生成了一个web网站,方便大家使用,地址:https://git.io/linux。由于网络的原因这个地址可能访问不到,大家可以访问:https://wangchujiang.com/linux-command。其中包含了Linux命令有: 不仅于此,作者还提供了微信小程序版、Chrome插件、Alfred 版本、Dash 版本、命令行工具等方式进行检索。下载方式请移步仓库地址。 仓库还整理了很多Linux学习资料,包括各种资讯、文章、技术,知识相关、软件工具等,是一个学习Linux的不错选择。 开源地址:https://github.com/jaywcjlove/linux-command 网站地址:https://wangchujiang.com/linux-command/ 更多文章请关注微信公众号: 治恒说说","categories":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/categories/Github/"}],"tags":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/tags/Github/"},{"name":"工具","slug":"工具","permalink":"https://dddreams.github.io/tags/工具/"}],"keywords":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/categories/Github/"}]},{"title":"github精选-PeaZip一款好用又免费的压缩软件","slug":"211120-github精选-PeaZip一款好用又免费的压缩软件","date":"2021-11-20T14:46:17.000Z","updated":"2021-11-20T14:19:41.721Z","comments":true,"path":"211120-github精选-PeaZip一款好用又免费的压缩软件.html","link":"","permalink":"https://dddreams.github.io/211120-github精选-PeaZip一款好用又免费的压缩软件.html","excerpt":"","text":"先问大家一个问题,你们电脑上常用的压缩软件是什么呢?WinRaR?还是BandiZip?或者国产的好压、还是360压缩?如果是后两者,那么我建议你是时候换一款压缩软件了。 1、介绍今天介绍的便是一款好用又免费而且没有广告的压缩软件PeaZip,跨平台Windows和Linux都可以运行,它本身有一种原生的压缩格式叫做 pea。除此之外,完全支持7Z, 7Z-sfx, BZip2, GZip/TGZ, PAQ8F, PAQ8JD, PAQ8L, PEA, QUAD, split (.001), TAR, ZIP等格式;支持浏览或解压缩以下格式:CE, ARJ, CAB, CHM, CPIO, ISO, Java archives (JAR, EAR, WAR), Linux installers (DEB, PET/PUP, RPM, SLP), LHA, LZH, Open Office file types, PAK/PK3/PK4, RAR, Windows installers (NSIS, some MSI), WIM, XPI, Z/TZ。这些格式不能支持压缩,说原因,大家也明白,这些格式都是不开放的。 2、软件界面界面长这样子 你可以轻松切换到中文界面,打开【Options】菜单选择【Localization】,然后选择chs.txt(简体中文)或者cht.txt(繁体中文)就变为下面这样了。 3、有哪些不错的功能1、智能解压 如果在一个压缩文件中包含很多文件,就像上图,当我们在未打开这个压缩软件时先点击「解压到当前文件夹」时,一定会在下一秒为刚刚做出的操作而后悔,因为这些文件迅速的占领了你的视野,就像下图这样: 但是如果遇到内置一个单独文件夹的压缩包时,当我们像以往一样使用「解压到(压缩文件名称的)文件夹」时就会遇到令人抓狂的「套娃」模式。这时,智能解压就显得十分重要。当解压一个我们不知道内部是什么的文件夹时,使用智能解压,就可以优雅地将压缩包中的目标文件整理到当前目录的同名文件夹里。不用担心是否会套娃,也不用担心一堆文件突然塞满你的文件夹。 2、快速拖放若你习惯于直接将压缩包内文件「拖出来」来进行解压操作,那你大概常常会在压缩软件的解压进度条走完后遇到又一个进度条:将操作文件从临时文件夹复制到目标拖放目录。 这种情况常见于从一个特别大的压缩文件里面提取一个小文件,在 WinRAR 上我们需要等待它完全解压、转存到临时文件夹,然后再让它自动把你要的文件放到你想要的位置上去……这一切无疑都太耗费时间了。 而 Peazip 在解压大文件时会使用「快速拖放」功能,让你的文件被直接解压到目标拖放目录,而非从临时文件夹处中转。既不需要你改变操作习惯,同时有效减少你的等待时间。 3、密码管理器 如果你有密码管理的需求,PeaZip 是一个不错的选择。 4、开源地址github地址:https://github.com/peazip/PeaZip 下载地址:https://peazip.github.io/ 更多文章请关注微信公众号: 治恒说说","categories":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/categories/Github/"}],"tags":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/tags/Github/"},{"name":"软件","slug":"软件","permalink":"https://dddreams.github.io/tags/软件/"}],"keywords":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/categories/Github/"}]},{"title":"使用Python读取Excel中的图片并对应到记录","slug":"211116-使用Python读取Excel中的图片并对应到记录","date":"2021-11-16T13:40:58.000Z","updated":"2021-11-16T12:34:59.631Z","comments":true,"path":"211116-使用Python读取Excel中的图片并对应到记录.html","link":"","permalink":"https://dddreams.github.io/211116-使用Python读取Excel中的图片并对应到记录.html","excerpt":"","text":"1、需求 有这样一个需求,将采集在 Excel 中的人员信息(包含照片)导入到 Mysql 库。需求很简单,就是读取 Excel 中的数据插入到 Mysql 中的表,问题在于照片怎么读取? 2、分析 对于读取文本数据,直接按行读取即可;对于图片,常用做法是,将 Excel 文件的后缀名改为 zip,然后解压文件,对应文件名目录下有一个\\xl\\media的目录,里面便是我们要照片的图片,然而,他的文件名、文件顺序都是乱的,确定不出图片对应的记录,怎么办呢?其实目录中还有这样一个 xml 文件\\xl\\drawings\\drawing1.xml其中就有图片与记录的对应关系,只要解析 xml 文件就可以了。知道了这个思路,那我们来一步步的实现他。 3、实现1、复制并修改指定目录下的文件类型名,将excel后缀名修改为.zip 12345678910111213141516def copy_change_file_name(file_path, new_type='.zip'): if not isfile_exist(file_path): return '' extend = os.path.splitext(file_path)[1] # 获取文件拓展名 if extend != '.xlsx' and extend != '.xls': print(\"It's not a excel file! %s\" % file_path) return False file_name = os.path.basename(file_path) # 获取文件名 new_name = str(file_name.split('.')[0]) + new_type # 新的文件名,命名为:xxx.zip dir_path = os.path.dirname(file_path) # 获取文件所在目录 new_path = os.path.join(dir_path, new_name) # 新的文件路径 if os.path.exists(new_path): os.remove(new_path) shutil.copyfile(file_path, new_path) return new_path # 返回新的文件路径,压缩包 2、解压文件 12345678910111213def unzip_file(zipfile_path): if not isfile_exist(zipfile_path): return False if os.path.splitext(zipfile_path)[1] != '.zip': print(\"It's not a zip file! %s\" % zipfile_path) return False file_zip = zipfile.ZipFile(zipfile_path, 'r') file_name = os.path.basename(zipfile_path) # 获取文件名 zipdir = os.path.join(os.path.dirname(zipfile_path), str(file_name.split('.')[0])) # 获取文件所在目录 for files in file_zip.namelist(): file_zip.extract(files, os.path.join(zipfile_path, zipdir)) # 解压到指定文件目录 file_zip.close() return True 3、读取解压后的文件夹,打印图片路径 12345678910111213141516def read_img(zipfile_path): img_dict = dict() if not isfile_exist(zipfile_path): return False dir_path = os.path.dirname(zipfile_path) # 获取文件所在目录 file_name = os.path.basename(zipfile_path) # 获取文件名 pic_dir = 'xl' + os.sep + 'media' # excel变成压缩包后,再解压,图片在media目录 pic_path = os.path.join(dir_path, str(file_name.split('.')[0]), pic_dir) file_list = os.listdir(pic_path) for file in file_list: filepath = os.path.join(pic_path, file) print(filepath) img_index = int(re.findall(r'image(\\d+)\\.', filepath)[0]) img_dict[img_index] = dict(img_index=img_index, img_path=filepath) return img_dict 4、解析xml 文件,获取图片在excel表格中的索引位置信息 123456789101112131415161718192021222324252627282930313233343536373839404142434445def get_img_pos_info(zip_file_path, img_dict, img_feature): \"\"\"解析xml 文件,获取图片在excel表格中的索引位置信息\"\"\" os.path.dirname(zip_file_path) dir_path = os.path.dirname(zip_file_path) # 获取文件所在目录 file_name = os.path.basename(zip_file_path) # 获取文件名 xml_dir = 'xl' + os.sep + 'drawings' + os.sep + 'drawing1.xml' xml_path = os.path.join(dir_path, str(file_name.split('.')[0]), xml_dir) image_info_dict = parse_xml(xml_path, img_dict, img_feature=img_feature) # 解析xml 文件, 返回图片索引位置信息 return image_info_dict# 重命名解压获取图片位置,及图片表格索引信息def get_img_info(excel_file_path, img_feature): if img_feature not in [\"img_index\", \"img_path\", \"img_base64\"]: raise Exception('图片返回参数错误, [\"img_index\", \"img_path\", \"img_base64\"]') zip_file_path = copy_change_file_name(excel_file_path) if zip_file_path != '': if unzip_file(zip_file_path): img_dict = read_img(zip_file_path) image_info_dict = get_img_pos_info(zip_file_path, img_dict, img_feature) return image_info_dict return dict()# 解析xml文件并获取对应图片位置def parse_xml(file_name, img_dict, img_feature='img_path'): # 得到文档对象 image_info = dict() dom_obj = xmldom.parse(file_name) # 得到元素对象 element = dom_obj.documentElement def _f(subElementObj): for anchor in subElementObj: xdr_from = anchor.getElementsByTagName('xdr:from')[0] col = xdr_from.childNodes[0].firstChild.data # 获取标签间的数据 row = xdr_from.childNodes[2].firstChild.data embed = \\ anchor.getElementsByTagName('xdr:pic')[0].getElementsByTagName('xdr:blipFill')[0].getElementsByTagName('a:blip')[0].getAttribute('r:embed') # 获取属性 image_info[(int(row), int(col))] = img_dict.get(int(embed.replace('rId', '')), {}).get(img_feature) sub_twoCellAnchor = element.getElementsByTagName(\"xdr:twoCellAnchor\") sub_oneCellAnchor = element.getElementsByTagName(\"xdr:oneCellAnchor\") _f(sub_twoCellAnchor) _f(sub_oneCellAnchor) return image_info 5、读取包含图片的excel数据 1234567891011121314151617181920212223def read_excel_info(file_path, img_col_index, img_feature='img_path'): img_info_dict = get_img_info(file_path, img_feature) book = xlrd.open_workbook(file_path) sheet = book.sheet_by_index(0) head = dict() for i, v in enumerate(sheet.row(0)): head[i] = v.value info_list = [] for row_num in range(sheet.nrows): d = dict() for col_num in range(sheet.ncols): if row_num == 0: continue if 'empty:' in sheet.cell(row_num, col_num).__str__(): if col_num in img_col_index: d[head[col_num]] = img_info_dict.get((row_num, col_num)) else: d[head[col_num]] = sheet.cell(row_num, col_num).value else: d[head[col_num]] = sheet.cell(row_num, col_num).value if d: info_list.append(d) return info_list 1234567891011import reimport xml.dom.minidom as xmldomimport osimport zipfileimport shutilimport xlrdif __name__ == '__main__': path = r'E:\\\\员工信息.xlsx' data = read_excel_info(path, img_col_index=[4]) print(data) 完整代码:https://github.com/dddreams/read-excel-image/blob/master/read_users.py 4、处理图片对应不到的问题 有时会出现图片对应不到记录的情况,这是因为 Excel 中图片不在单独的单元格内,指定的列中取不到图片,这就需要调整 Excel 内的数据了,但是如果数据量比较大,调整起来就比较麻烦了。其实,我们可以借助 Excel 宏的功能,批量修改图片大小,批量让其居中,位于单元格内部,这样在解析 xml 时就能对应到每条记录了。 打开 Excel 按下 Alt + F11 ,然后点击【插入】菜单,选择【模块】复制下面代码: 1234567Sub dq()Dim shp As ShapeFor Each shp In ActiveSheet.Shapesshp.Left = (shp.TopLeftCell.Width - shp.Width) / 2 + shp.TopLeftCell.Leftshp.Top = (shp.TopLeftCell.Height - shp.Height) / 2 + shp.TopLeftCell.TopNextEnd Sub 然后返回 Excel 【开始】菜单中点击【查找和选择】选择【定位条件】中【对象】,将选中所有的图片,然后按下Alt+F8 点击【执行】就可以批量将图片在所在单元格居中了。如果图片大小过大,全部选中后更改图片大小即可。然后解析 Xml 文件就不会出现图片与记录对应不到的情况了。 更多文章请关注微信公众号: 治恒说说","categories":[{"name":"Python","slug":"Python","permalink":"https://dddreams.github.io/categories/Python/"}],"tags":[{"name":"Python","slug":"Python","permalink":"https://dddreams.github.io/tags/Python/"},{"name":"Excel","slug":"Excel","permalink":"https://dddreams.github.io/tags/Excel/"}],"keywords":[{"name":"Python","slug":"Python","permalink":"https://dddreams.github.io/categories/Python/"}]},{"title":"Spring RestTemplate的使用","slug":"211111-SpringRestTemplate的使用","date":"2021-11-11T13:40:58.000Z","updated":"2021-11-11T13:50:51.352Z","comments":true,"path":"211111-SpringRestTemplate的使用.html","link":"","permalink":"https://dddreams.github.io/211111-SpringRestTemplate的使用.html","excerpt":"","text":"实际工作中我们经常会遇到利用 java 去访问其他接口获取数据,常用的有 JDK 原生的 HttpURLConnection 无需依赖其他包,提供了很多方法;另外 HttpClient 也是我们的不二选择,在 spring 之前,我们经常会用 HttpClient 来处理;还有一种方式就是利用 Socket,使用起来比较麻烦;用了 spring 之后,RestTemplate 则更加方便,更加灵活。 一、概述spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。 在Spring应用程序中访问第三方REST服务与使用Spring RestTemplate类有关。RestTemplate类的设计原则与许多其他Spring 模板类(例如JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。 RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。 考虑到RestTemplate类是为调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连就不足为奇了,后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,RestTemplate类具有headForHeaders()、getForObject()、postForObject()、put()和delete()等方法。 二、实现最新api地址:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html RestTemplate包含以下几个部分: HttpMessageConverter 对象转换器ClientHttpRequestFactory 默认是JDK的HttpURLConnectionResponseErrorHandler 异常处理ClientHttpRequestInterceptor 请求拦截器 常规配置12345678public MyRestClientService(RestTemplateBuilder restTemplateBuilder) { this.restTemplate = restTemplateBuilder .basicAuthorization(\"username\", \"password\") .setConnectTimeout(3000) .setReadTimeout(5000) .rootUri(\"http://api.example.com/\") .build(); } ClientHttpRequestInterceptor学习使用带有Spring RestTemplate的ClientHttpRequestInterceptor,以Spring AOP风格记录请求和响应头和主体。 拦截器记录请求和响应1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.http.HttpRequest;import org.springframework.http.client.ClientHttpRequestExecution;import org.springframework.http.client.ClientHttpRequestInterceptor;import org.springframework.http.client.ClientHttpResponse;import org.springframework.util.StreamUtils; import java.io.IOException;import java.nio.charset.Charset; public class RequestResponseLoggingInterceptor implements ClientHttpRequestInterceptor { private final Logger log = LoggerFactory.getLogger(this.getClass()); @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { logRequest(request, body); ClientHttpResponse response = execution.execute(request, body); logResponse(response); //Add optional additional headers response.getHeaders().add(\"headerName\", \"VALUE\"); return response; } private void logRequest(HttpRequest request, byte[] body) throws IOException { if (log.isDebugEnabled()) { log.debug(\"===========================request begin================================================\"); log.debug(\"URI : {}\", request.getURI()); log.debug(\"Method : {}\", request.getMethod()); log.debug(\"Headers : {}\", request.getHeaders()); log.debug(\"Request body: {}\", new String(body, \"UTF-8\")); log.debug(\"==========================request end================================================\"); } } private void logResponse(ClientHttpResponse response) throws IOException { if (log.isDebugEnabled()) { log.debug(\"============================response begin==========================================\"); log.debug(\"Status code : {}\", response.getStatusCode()); log.debug(\"Status text : {}\", response.getStatusText()); log.debug(\"Headers : {}\", response.getHeaders()); log.debug(\"Response body: {}\", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset())); log.debug(\"=======================response end=================================================\"); } }} 注册ClientHttpRequestInterceptor1234567891011@Beanpublic RestTemplate restTemplate(){ RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(newBufferingClientHttpRequestFactory(clientHttpRequestFactory())); restTemplate.setMessageConverters(Collections.singletonList(mappingJacksonHttpMessageConverter())); restTemplate.setInterceptors( Collections.singletonList(newRequestResponseLoggingInterceptor()) ); return restTemplate;} 三、使用GET12345678private static void getEmployees(){ final String uri = \"http://localhost:8080/springrestexample/employees\"; RestTemplate restTemplate = new RestTemplate(); String result = restTemplate.getForObject(uri, String.class); System.out.println(result);} 使用RestTemplate定制HTTP头文件12345678910111213private static void getEmployees(){ final String uri = \"http://localhost:8080/springrestexample/employees\"; RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); HttpEntity<String> entity = new HttpEntity<String>(\"parameters\", headers); ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class); System.out.println(result);} Get请求获取响应为一个对象12345678private static void getEmployees(){ final String uri = \"http://localhost:8080/springrestexample/employees\"; RestTemplate restTemplate = new RestTemplate(); EmployeeListVO result = restTemplate.getForObject(uri, EmployeeListVO.class); System.out.println(result);} URL 参数1234567891011private static void getEmployeeById(){ final String uri = \"http://localhost:8080/springrestexample/employees/{id}\"; Map<String, String> params = new HashMap<String, String>(); params.put(\"id\", \"1\"); RestTemplate restTemplate = new RestTemplate(); EmployeeVO result = restTemplate.getForObject(uri, EmployeeVO.class, params); System.out.println(result);} POST12345678910private static void createEmployee(){ final String uri = \"http://localhost:8080/springrestexample/employees\"; EmployeeVO newEmployee = new EmployeeVO(-1, \"Adam\", \"Gilly\", \"[email protected]\"); RestTemplate restTemplate = new RestTemplate(); EmployeeVO result = restTemplate.postForObject( uri, newEmployee, EmployeeVO.class); System.out.println(result);} Submit Form Data1234567891011HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap<String, String> map= new LinkedMultiValueMap<>();map.add(\"id\", \"1\"); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); RestTemplate restTemplate = new RestTemplate();EmployeeVO result = restTemplate.postForObject( uri, request, EmployeeVO.class);System.out.println(result); PUT1234567891011private static void updateEmployee(){ final String uri = \"http://localhost:8080/springrestexample/employees/{id}\"; Map<String, String> params = new HashMap<String, String>(); params.put(\"id\", \"2\"); EmployeeVO updatedEmployee = new EmployeeVO(2, \"New Name\", \"Gilly\", \"[email protected]\"); RestTemplate restTemplate = new RestTemplate(); restTemplate.put ( uri, updatedEmployee, params);} Simple PUT123456Foo updatedInstance = new Foo(\"newName\");updatedInstance.setId(createResponse.getBody().getId());String resourceUrl = fooResourceUrl + '/' + createResponse.getBody().getId();HttpEntity<Foo> requestUpdate = new HttpEntity<>(updatedInstance, headers);template.exchange(resourceUrl, HttpMethod.PUT, requestUpdate, Void.class); 使用.exchange和请求回调12345678910RequestCallback requestCallback(final Foo updatedInstance) { return clientHttpRequest -> { ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(clientHttpRequest.getBody(), updatedInstance); clientHttpRequest.getHeaders().add( HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); clientHttpRequest.getHeaders().add( HttpHeaders.AUTHORIZATION, \"Basic \" + getBase64EncodedLogPass()); };} DELETE123456789private static void deleteEmployee(){ final String uri = \"http://localhost:8080/springrestexample/employees/{id}\"; Map<String, String> params = new HashMap<String, String>(); params.put(\"id\", \"2\"); RestTemplate restTemplate = new RestTemplate(); restTemplate.delete ( uri, params );} 以上便是 Spring RestTemplate 在实际开发中使用的方法,请笑纳吧! 更多文章请关注微信公众号: 治恒说说","categories":[],"tags":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/tags/Spring-Boot/"}],"keywords":[]},{"title":"github精选-秒杀系统设计与实现","slug":"211108-github精选-秒杀系统设计与实现","date":"2021-11-08T14:46:17.000Z","updated":"2021-11-08T14:49:23.372Z","comments":true,"path":"211108-github精选-秒杀系统设计与实现.html","link":"","permalink":"https://dddreams.github.io/211108-github精选-秒杀系统设计与实现.html","excerpt":"","text":"很长时间没有更新了,最近有个想法,做一个github 开源项目精选的主题,我经常会去逛 githb,发现一些好玩或实用的项目就会 star,后来发现 star 了很多项目,但在实际要使用时还是想不起来自己已经 star 了该项目了。所以就有了这样一个想法,整理记录 github 上的一些项目,知道它是做什么的,它能解决什么问题,当实际使用时直接在博客上搜索,岂不是很方便了;于是就有了这个 github精选的分类。 今天分享的是一个秒杀系统设计与实现,互联网工程师进阶与分析,创建者对高并发大流量如何进行秒杀架构这部分知识做了一个系统的整理。目前已获得 22.9k 的star,其中包含整体设计,数据库设计和常见的一些问题整理,涉及到的技术有 SpringBoot、MQ、Redis、Dubbo、ZK、Maven、lua 等技术。 我花了一些时间看了其中的内容,真的是干货,nginx优化,mysql 的执行计划,数据的备份和恢复写的都很实用,程序员的进阶修炼不可缺少的好项目,极力推荐。 送上开源地址:https://github.com/qiurunze123/miaosha 更多文章请关注微信公众号: 治恒说说","categories":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/categories/Github/"}],"tags":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/tags/Github/"},{"name":"教程","slug":"教程","permalink":"https://dddreams.github.io/tags/教程/"}],"keywords":[{"name":"Github","slug":"Github","permalink":"https://dddreams.github.io/categories/Github/"}]},{"title":"Mysql登录失败多次锁定配置","slug":"210208-Mysql登录失败多次锁定配置","date":"2021-02-08T00:54:26.000Z","updated":"2021-11-07T14:29:39.803Z","comments":true,"path":"210208-Mysql登录失败多次锁定配置.html","link":"","permalink":"https://dddreams.github.io/210208-Mysql登录失败多次锁定配置.html","excerpt":"","text":"大家有没有发现,现在我们用的手机,网站只要需要登录或者认证的,都有这样一项功能,密码或口令输错几次后系统锁定N分钟,并提示N分钟过后重试,有时候会很头疼,万一忘记了密码,只能填一堆信息重新获取,当然这也是对安全的考虑,其实在等保测评中,也有类似的要求,今天我们来学习一下MySQL是如何设置登录多次失败锁定的。 Tips:示例是以 Mysql 5.7 为例 需求Mysql 数据库密码数据 3 次,自动锁定15分钟。 查看配置首先使用下面命令查看参数是否设置1show variables like '%connection_control%'; 如果看到Empty set (0.01 sec) 的字样,说明没有设置该参数;再查看是否安装 CONNECTION_CONTROL 和 CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS 插件。 CONNECTION_CONTROL:用来控制登录失败的次数及延迟响应时间。 CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS:该表将登录失败的操作记录至IS库中。 1show plugins; 如果插件列表中没有【图一】中的勾出的两项,需要安装插件; 安装插件12install plugin CONNECTION_CONTROL soname 'connection_control.so';install plugin CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS soname 'connection_control.so'; 执行完之后,再次查看插件列表,会出现【图一】中勾出的两项;现在再次查看connection_control的参数:1show variables like '%connection_control%'; 可以看到已有的默认配置: connection_control_failed_connections_threshold:失败尝试的次数,默认为3,表示当连接失败3次后启用连接控制,0表示不开启。 connection_control_max_connection_delay:响应延迟的最大时间,默认约25天 connection_control_min_connection_delay:响应延迟的最小时间,默认1000微秒,1秒 配置方式一:修改配置文件 my.cnf1234vim /etc/my.cnf# 添加下面两项connection-control-failed-connections-threshold=3 #登陆失败次数限制connection-control-min-connection-delay=900000 #限制重试时间,此处为毫秒,注意按需求换算 重启 MySQL 服务service mysqld restart 或 service mysql restart 方式二:设置全局变量12SET GLOBAL connection_control_failed_connections_threshold = 3;SET GLOBAL connection_control_min_connection_delay = 900000; 配置完成,再次使用show variables like '%connection_control%';查看参数:可以看到已经是我们配置的值了。 验证如【图四】输错三次密码之后,第四次程序已经锁住了。 参考和相关链接MySQL数据库限制多次登录失败重试时间:https://blog.csdn.net/ywd1992/article/details/83865537 MySQL 插件之连接控制插件:https://www.cnblogs.com/zhenxing/p/11050823.html 更多文章请关注微信公众号: 治恒说说 如果文章对你有用,转发分享、赞赏才是真爱 [斜眼笑]","categories":[{"name":"Mysql","slug":"Mysql","permalink":"https://dddreams.github.io/categories/Mysql/"}],"tags":[{"name":"运维","slug":"运维","permalink":"https://dddreams.github.io/tags/运维/"},{"name":"Mysql","slug":"Mysql","permalink":"https://dddreams.github.io/tags/Mysql/"}],"keywords":[{"name":"Mysql","slug":"Mysql","permalink":"https://dddreams.github.io/categories/Mysql/"}]},{"title":"SpringBoot-使用JdbcTemplate操作数据库","slug":"210206-SpringBoot-使用JdbcTemplate操作数据库","date":"2021-02-06T08:14:25.000Z","updated":"2021-02-06T08:42:47.332Z","comments":true,"path":"210206-SpringBoot-使用JdbcTemplate操作数据库.html","link":"","permalink":"https://dddreams.github.io/210206-SpringBoot-使用JdbcTemplate操作数据库.html","excerpt":"","text":"关于 JDBC 肯定是 java 开发者的入门知识,很显然在 Spring boot 中的使用也是非常简单的,这一节先当与给大家复习了,已熟知的同学请忽略。 Tips:示例中 MySQL 使用5.7版本,spring boot 使用 2.4.2版本 加依赖1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId></dependency> 请注意这里使用的包是spring-boot-starter-jdbc,其实在使用中你会发现会有这样一个包spring-boot-starter-data-jdbc,那么他们是什么区别呢? 区别在于,前者是 Spring支持JDBC数据库的包,使用DataSourceTransactionManager管理事务;后者是 Jpa 对JDBC的支持,使用JpaTransactionManager管理事务。至于 Jpa,后面会有相关的文章介绍。 写配置12345678910server: port: 8080spring: application: name: spring-boot-jdbc datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/test username: shure password: shure 要想访问数据库,那肯定要配置数据库的连接信息了注意:mysql8.0的驱动包有所变化 com.mysql.cj.jdbc.Driver 加注解无需注解 编码首先在mysql数据库中建立 user 的数据表。123456789DROP TABLE IF EXISTS `t_user`;CREATE TABLE `t_user` ( `id` int(8) unsigned NOT NULL, `name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL, `age` int NOT NULL, `gender` varchar(8) COLLATE utf8mb4_general_ci NOT NULL, `address` varchar(500) COLLATE utf8mb4_general_ci NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; 然后,建立 user 表对应的实体类,并添加构造函数。123456789101112131415@Datapublic class UserEntity { private Integer id; private String name; private int age; private boolean gender; private String address; public UserEntity(){} public UserEntity(String name, int age, boolean gender, String address){ this.name = name; this.age = age; this.gender = gender; this.address = address; }} 再然后,编写访问数据库的操作。123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051@Repositorypublic class UserJdbcDao { @Autowired private JdbcTemplate jdbcTemplate; public List<UserEntity> findAll(String name){ List<UserEntity> list = jdbcTemplate.query(\"SELECT * FROM t_user WHERE name = ?\", (res, i) -> { UserEntity user = new UserEntity(); user.setId(res.getInt(\"ID\")); user.setName(res.getString(\"NAME\")); user.setAddress(res.getString(\"ADDRESS\")); user.setAge(res.getInt(\"AGE\")); user.setGender(res.getBoolean(\"GENDER\")); return user; }, name); return list; } public UserEntity getUser(Integer id){ List<UserEntity> list = jdbcTemplate.query(\"SELECT * FROM t_user WHERE id = ?\", (res, i) -> { UserEntity user = new UserEntity(); user.setId(res.getInt(\"ID\")); user.setName(res.getString(\"NAME\")); user.setAddress(res.getString(\"ADDRESS\")); user.setAge(res.getInt(\"AGE\")); user.setGender(res.getBoolean(\"GENDER\")); return user; }, id); return list.get(0); } public Integer save(UserEntity user){ return jdbcTemplate.update(\"INSERT INTO t_user(name, age, gender, address) VALUES(?, ?, ?, ?)\", user.getName(), user.getAge(), user.isGender(), user.getAddress()); } public void update(UserEntity user){ jdbcTemplate.update(\"UPDATE t_user SET name = ?, age = ?, gender = ?, address = ? WHERE id = ?\", user.getName(), user.getAge(), user.isGender(), user.getAddress(), user.getId()); } public void delete(Integer id){ jdbcTemplate.update(\"DELETE FROM t_user WHERE id = ?\", id); }} 最后,编写单元测试用例,创建对UserService的单元测试用例,通过创建、删除和查询来验证数据库操作的正确性。 这样 jdbc 的基本操作就完成了,通过上面这个简单的例子,我们可以看到在Spring Boot下访问数据库的配置依然秉承了框架的初衷:简单。我们只需要在pom.xml中加入数据库依赖,再到application.properties中配置连接信息,不需要像Spring应用中创建JdbcTemplate的Bean,就可以直接在自己的对象中注入使用。 参考与相关链接示例代码:https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-jdbc 程序员DD的博文:https://blog.didispace.com/spring-boot-learning-21-3-1/ 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、赞赏才是真爱 [斜眼笑]","categories":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}],"tags":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/tags/Spring-Boot/"}],"keywords":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}]},{"title":"Mysql密码安全策略配置","slug":"210204-Mysql密码安全策略配置","date":"2021-02-04T06:49:39.000Z","updated":"2021-02-06T08:43:00.410Z","comments":true,"path":"210204-Mysql密码安全策略配置.html","link":"","permalink":"https://dddreams.github.io/210204-Mysql密码安全策略配置.html","excerpt":"","text":"近些年来,网络安全相应的法律法规在逐步完善,网络安全等级保护制度在《网络安全法》的正式实施后,得到大力推进。很多公司、单位,逐渐认识到网络安全等级保护的重要性,为了落实网络安全等级保护工作,我所在的项目最近也做了一次等保的测评,下面是针对 MySQL 数据库相关的一些整改配置。 Tips:示例是以 Mysql 5.7 为例 密码策略首先登录 Mysql 查看目前数据库的密码策略1show variables like 'validate_password%'; 如果出现 Empty set (0.01 sec) 的字样,说明你的 Mysql 还未安装 validate_password 的插件。查看插件列表:1show plugins; 如果没有这一项,说明确实没有安装validate_password插件。 安装插件要使服务器可用,插件库文件必须位于 MySQL 插件目录中(由plugin_dir系统变量命名的目录)。如果需要,通过在服务器启动时设置 plugin_dir 的值来配置插件目录位置。1show variables like 'plugin_dir'; 插件库的文件名是 validate_password.文件名后缀根据平台的不同而不同(.so 是Unix和类Linux系统,.dll是Windows系统)。要在服务器启动时加载插件,可以在配置文件中使用 plugin-load-add 参数来命名包含它的库文件。修改配置文件my.cnf123456vim /etc/my.cnf[mysqld]plugin-load-add=validate_password.so#服务器在启动时加载插件,并防止在服务器运行时删除插件。validate-password=FORCE_PLUS_PERMANENT 修改 my.cnf 之后,重启服务器以使新设置生效。或者,要在运行时注册插件,可以使用以下命令:123service mysqld restart#或install plugin validate_password soname 'validate_password.so'; 然后在次查看插件列表,就会出现图一中的那一项 validate_password 配置再次执行查看密码策略的命令:1show variables like 'validate_password%'; validate_password_check_user_name:设置为ON的时候表示能将密码设置成当前用户名。validate_password_dictionary_file:用于检查密码的字典文件的路径名。validate_password_length:所需密码的最小长度。validate_password_mixed_case_count:默认值为1,如果密码策略是中等或更强的,限制小写字符和大写字符个数。validate_password_number_count:如果密码策略是中等或更强的,要求密码具有数字的个数。validate_password_policy: 默认值为1, 密码强度等级 [LOW:0| MEDIUM:1 | STRONG:2]validate_password_special_char_count : 默认值为1,限制特殊字符个数其中,validate_password_policy0/LOW:只检查长度。1/MEDIUM:检查长度、数字、大小写、特殊字符。2/STRONG:检查长度、数字、大小写、特殊字符字典文件。 可以使用一下命令修改上述值:123set global validate_password_policy=1;set global validate_password_length=8;flush privileges; 也可以在配置文件中直接配置:12345vim /etc/my.cnf[mysqld]validate_password_policy=1validate_password_length=8 参考和相关链接mysql 5.7安装密码校验插件validate_password:https://www.cnblogs.com/alonely/p/10927541.html MYSQL57密码策略修改:https://www.cnblogs.com/zhi-leaf/p/5994478.html 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、赞赏才是真爱 [斜眼笑]","categories":[{"name":"Mysql","slug":"Mysql","permalink":"https://dddreams.github.io/categories/Mysql/"}],"tags":[{"name":"运维","slug":"运维","permalink":"https://dddreams.github.io/tags/运维/"},{"name":"Mysql","slug":"Mysql","permalink":"https://dddreams.github.io/tags/Mysql/"}],"keywords":[{"name":"Mysql","slug":"Mysql","permalink":"https://dddreams.github.io/categories/Mysql/"}]},{"title":"SpringBoot-集成Swagger2文档生成","slug":"210127-SpringBoot-集成Swagger2文档生成","date":"2021-01-27T14:26:00.000Z","updated":"2021-02-06T08:42:54.238Z","comments":true,"path":"210127-SpringBoot-集成Swagger2文档生成.html","link":"","permalink":"https://dddreams.github.io/210127-SpringBoot-集成Swagger2文档生成.html","excerpt":"","text":"随着前后端分离和微服务架构的流行,甚至有些公司强制使用微服务架构,在提高效率同时,问题也随之而来。通常我们的一个RESTful API,会对应不同的开发者,Android,IOS,Web,小程序等不同团队研发人员,显然沟通成本成倍的增长,一般我们都会通过共享文档来维护这些 API ,但随着项目中 API 越来越多,版本升级越多,就会造成文档维护不及时或者无人维护的问题。如果你所在团队也正在面临这样的问题,那么今天我们一起来学习文档神器 Swagger。 介绍Swagger 是一款 RESTful 接口的文档在线自动生成外加调试功能的软件。可以将项目的所有接口在一个UI界面上展示出来,同时表明了这个接口的用途,接口需要的参数是什么类型参数是否必须,输入了参数可以直接测试接口类似postman的功能,会显示接口请求的状态码和返回的数据结构。 那么后端开发人员只需要在代码中添加几个注解,就会生成一套标准的文档,而且随着代码的变动也随之更新,这样减少了单独维护文档成本,也减少了团队之间的沟通成本,接下来我们来看怎么将 Swagger 集成到 Spring boot 中。 加依赖需要加入 swagger2 和 swagger-ui 的包12345678910<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version></dependency><dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version></dependency> 写配置12swagger: enable: true 注意:在生产环境将此配置设置为 false 因为使用原生的 swagger,需要额外加入swagger 的配置类1234567891011121314151617181920212223@Configuration@EnableSwagger2@ConditionalOnProperty(name = \"swagger.enable\", havingValue = \"true\")public class SwaggerConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage(\"com.shure.swagger2\")) .paths(PathSelectors.any()) .build(); } //配置在线文档的基本信息 private ApiInfo apiInfo() { return new ApiInfoBuilder() .title(\"springboot利用swagger构建api文档\") .description(\"用户管理接口文档\") .termsOfServiceUrl(\"https://dddreams.github.io/\") .version(\"0.1\") .build(); }} 加注解在启动类加 @EnableSwagger2注解,或者在上面 SwaggerConfig 类上加入此注解。1234567@SpringBootApplication@EnableSwagger2public class SpringBootSwagger2Application { public static void main(String[] args) { SpringApplication.run(SpringBootSwagger2Application.class, args); }} 如何使用OK,完成上面三步,已经可以使用 swagger 了,启动后访问,http://localhost:8080/swagger-ui.html 编写业务接口新建用户的实体类 UserEntity,为了简化代码我们会引入 lombok 的插件。1234567891011121314@Data@ApiModel(description=\"用户实体\")public class UserEntity { @ApiModelProperty(\"用户编号\") private Integer id; @ApiModelProperty(\"用户姓名\") private String name; @ApiModelProperty(\"用户年龄\") private int age; @ApiModelProperty(\"用户性别\") private boolean gender; @ApiModelProperty(\"住址\") private String address;} 新建用户相关的 API 接口12345678910111213141516171819202122232425262728293031@Api(tags = \"1-用户管理\")@RestController@RequestMapping(\"/user\")public class UserController { // 模拟users信息的存储 static Map<Integer, UserEntity> users = Collections.synchronizedMap(new HashMap<>()); @GetMapping(\"/list\") @ApiOperation(value = \"获取用户列表\") public List<UserEntity> list(){ List<UserEntity> us = new ArrayList<>(users.values()); return us; } @GetMapping(\"/get/{id}\") @ApiOperation(value = \"获取用户详细信息\", notes = \"根据用户id来获取用户详细信息\") public UserEntity getUser(@PathVariable Integer id) { return users.get(id); } @PostMapping(\"/saveOrUpdate\") @ApiOperation(value = \"创建用户\", notes = \"根据User对象创建用户\") public String postUser(@RequestBody UserEntity user) { users.put(user.getId(), user); return \"success\"; } @DeleteMapping(\"/del/{id}\") @ApiOperation(value = \"删除用户\", notes = \"根据用户的id来指定删除对象\") public String deleteUser(@PathVariable Integer id) { users.remove(id); return \"success\"; }} 查看接口文档再来看看 swagger 的页面会发生什么变化。 展开标签后,会看到每个接口的详细信息,并且可以在线调试,返回结果的数据结构也一目了然。还包括实体的信息也会在文档中展现。 接口及方法的分类排序随着业务的增长,API 接口数量的增长,实际使用中我们会发现找一个接口会非常困难,这就考虑为接口和方法进行分类排序,方便查找。 接口的分类对于接口的分类,swagger 提供了 tags 的参数,只需在 Controller 的 @Api 注解上加参数 tags 即可。12345678910111213@Api(tags = \"用户管理\")@RestController@RequestMapping(\"/user\")public class UserController { ...}@Api(tags = {\"商品管理\", \"...\"})@RestController@RequestMapping(\"/goods\")public class GoodsController { ... } 可以将同类的接口,指定在 tags 中,文档中就会出现一个分类中。 接口的排序对于接口的排序,swagger 默认是根据字母顺序排的,并且只提供了这一种排序方式,我们可以投机取巧在 tags 参数的前面添加数字让其按顺序排列。12345678910111213@Api(tags = \"1-用户管理\")@RestController@RequestMapping(\"/user\")public class UserController { ...}@Api(tags = {\"2-商品管理\"})@RestController@RequestMapping(\"/goods\")public class GoodsController { ... } 方法的排序方式一,swagger 默认是根据字母顺序排的,可以通过以下方式:图片来源于 程序猿DD 的博客:https://blog.didispace.com/spring-boot-learning-21-2-4/ 方式二,引入 spring4all 社区开源的 swagger-spring-boot-startergithub地址:https://github.com/SpringForAll/spring-boot-starter-swagger12345<dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> <version>1.9.1.RELEASE</version></dependency> 添加配置:1swagger.ui-config.operations-sorter=alpha 方法的排序 swagger 提供了两个配置,alpha和method,默认为alpha。 方式三,引入 knife4j 开源的,knife4j-spring-ui基于bootstrap的ui框架。 github地址:https://github.com/xiaoymin/swagger-bootstrap-ui gitee地址:https://gitee.com/xiaoym/knife4j/tree/master/swagger-bootstrap-ui12345<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>${lastVersion}</version></dependency> 使用该方式需要在 SwaggerConfig 配置类上添加 @EnableSwaggerBootstrapUI注解,然后在方法上添加@ApiOperationSupport(order = 1)注解,并指定 order 的值。1234567891011121314@GetMapping(\"/list\")@ApiOperation(value = \"获取用户列表\")@ApiOperationSupport(order = 1)public List<UserEntity> list(){ List<UserEntity> us = new ArrayList<>(users.values()); return us;}@GetMapping(\"/get/{id}\")@ApiOperation(value = \"获取用户详细信息\", notes = \"根据用户id来获取用户详细信息\")@ApiOperationSupport(order = 2)public UserEntity getUser(@PathVariable Integer id) { return users.get(id);} 启动程序,访问:http://localhost:8080/doc.html,界面长这个样子。 注意:需要在个性化设置中开启 启用SwaggerBootstrapUi提供的增强功能 这样你就会发现,方法的排序按照我们设置的 order 排序了。 参数的排序参数的排序比较简单,只需在实体字段的注解上加 position 属性,参数就可以根据设置的position值进行排序了。12345678910@Data@ApiModel(description=\"商品实体\")public class GoodsEntity { @ApiModelProperty(value = \"商品编号\", position = 1) private Long id; @ApiModelProperty(value = \"商品名称\", position = 2) private String name; @ApiModelProperty(value = \"商品描述\", position = 3) private String desc;} 参考和相关链接示例代码:https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-swagger2 Swagger官网:https://swagger.io/ Swagger接口分类与各元素排序问题详解:https://blog.didispace.com/spring-boot-learning-21-2-4/ 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、赞赏才是真爱 [斜眼笑]","categories":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}],"tags":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/tags/Spring-Boot/"}],"keywords":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}]},{"title":"SpringBoot-集成Quartz作业调度","slug":"210125-SpringBoot-集成Quartz作业调度","date":"2021-01-25T08:41:46.000Z","updated":"2021-01-26T06:30:22.058Z","comments":true,"path":"210125-SpringBoot-集成Quartz作业调度.html","link":"","permalink":"https://dddreams.github.io/210125-SpringBoot-集成Quartz作业调度.html","excerpt":"","text":"在说 quartz 之前,我们先回顾一下 spring 的定时任务,使用相当简单,默认集成在 spring boot 中,所以在 spring boot 项目中无需额外添加依赖,无需配置,只需要加个注解就可以了,当然也可以实现动态添加删除定时任务,详情前往上一篇博文SpringBoot-定时任务,那为什么要使用 quartz 呢,主要还是考虑分布式的应用,下面我们就来看一下 spring boot 是怎么集成 quartz 的。 介绍简单介绍下 quartz,Quartz是OpenSymphony开源组织在任务调度领域的一个开源项目,完全基于Java实现。作为一个优秀的开源调度框架,Quartz具有以下特点: (1)强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求; (2)灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式; (3)分布式和集群能力。 Tips还记得 spring boot 的三板斧吗?加依赖,写配置,添注解 加依赖引入 spring-boot-starter-quartz 的依赖12345<!--quartz定时任务依赖--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId></dependency> 写配置无需配置 加注解无需注解 实现我们需要新建一个 QuartzService 的类,由于代码较长,在这里就不贴了,想看具体代码,请前往QuartzService.java。quartz 的 Job 实现方式很多,不一定要用这种方式,只是笔者认为这是一种比较简单的实现。 新加一个测试类 QuartzTest12345678910111213141516@SpringBootTestpublic class QuartzTest { private static final Logger logger = LoggerFactory.getLogger(QuartzTest.class); @Autowired private QuartzService quartzService; @Test public void quartzTest() { logger.info(\"添加定时任务\"); String jobName = \"test-1\"; Map<String, Object> map = new HashMap<>(); map.put(\"test\", \"测试任务执行\"); map.put(\"name\", jobName); quartzService.deleteJob(jobName, \"test\"); quartzService.addJob(TestQuartz.class, jobName, \"test\", \"0 */2 * * * ?\", map); }} 新建 Job 类,需要继承 QuartzJobBean123456789101112public class TestQuartz extends QuartzJobBean { private static final Logger logger = LoggerFactory.getLogger(TestQuartz.class); @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { SimpleDateFormat formatter = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\"); logger.info(\"任务开始执行:\" + formatter.format(System.currentTimeMillis())); JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap(); String test = jobDataMap.get(\"test\").toString(); String jobName = jobDataMap.get(\"name\").toString(); logger.info(test + \":\" + jobName); }} 启动,运行 Test 类,便可添加一个任务,创建 Job 时需要的参数可以通过 JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();来获取,这样就完成了一个定时任务功能。 Quartz 集群使用一个 Quartz 集群中的每个节点是一个独立的 Quartz 应用,它又管理着其他的节点。这就意味着你必须对每个节点分别启动或停止。Quartz 集群中,独立的 Quartz 节点并不与另一其的节点或是管理节点通信,而是通过相同的数据库表来感知到另一 Quartz 应用的。 因为 Quartz 集群依赖于数据库,所以必须首先创建 Quartz 数据库表,Quartz 发布包中包括了所有被支持的数据库平台的SQL脚本。这些SQL脚本存放于 <quartz_home>/docs/dbTables 目录下,总共12张表,不同版本,表个数可能不同。下面是具体表的说明: qrtz_blob_triggers : 以Blob 类型存储的触发器。 qrtz_calendars:存放日历信息, quartz可配置一个日历来指定一个时间范围。 qrtz_cron_triggers:存放cron类型的触发器。 qrtz_fired_triggers:存放已触发的触发器。 qrtz_job_details:存放一个jobDetail信息。 qrtz_job_listeners:job监听器。 qrtz_locks: 存储程序的悲观锁的信息(假如使用了悲观锁)。 qrtz_paused_trigger_graps:存放暂停掉的触发器。 qrtz_scheduler_state:调度器状态。 qrtz_simple_triggers:简单触发器的信息。 qrtz_trigger_listeners:触发器监听器。 qrtz_triggers:触发器的基本信息。 接下来,新建 quartz.yml 的配置文件,来覆盖默认的配置。123456789101112131415161718192021222324252627org: quartz: jobStore: useProperties: false tablePrefix: qrtz_ # 开启集群模式 isClustered: true # 集群实例检测时间间隔 ms clusterCheckinInterval: 5000 # misfire 任务的超时阈值 ms misfireThreshold: 60000 txIsolationLevelReadCommitted: true class: org.quartz.impl.jdbcjobstore.JobStoreTX driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate scheduler: instanceId: AUTO rmi.export: false rmi.proxy: false wrapJobExecutionInUserTransaction: false # 工作线程的线程池设置 threadPool: class: org.quartz.simpl.SimpleThreadPool threadCount: 25 threadPriority: 5 threadsInheritContextClassLoaderOfInitializingThread: true 这样 Quartz 集群就可以实现了。 扩展1、Quartz 触发时间配置的三种方式 cron 方式:采用cronExpression表达式配置时间。 simple 方式:和JavaTimer差不多,可以指定一个开始时间和结束时间外加一个循环时间。 calendars 方式:可以和cron配合使用,用cron表达式指定一个触发时间规律,用calendar指定一个范围。 注意:cron方式需要用到的4张数据表: qrtz_triggers,qrtz_cron_triggers,qrtz_fired_triggers,qrtz_job_details 2、使用 quartz 遇到的问题2.1、在定时任务执行中 service @Autowired 注解不进来创建 JobFactory 的 Bean,并在 SchedulerConfig 中添加到 SchedulerFactoryBean12345678910111213141516// JobFactory@Componentpublic class JobFactory extends SpringBeanJobFactory implements ApplicationContextAware { @Autowired private transient AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(final ApplicationContext context) { beanFactory = context.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); return job; }} SchedulerConfig.java123456789101112131415161718192021222324@Configurationpublic class SchedulerConfig { @Autowired private DataSource dataSource; @Autowired private JobFactory jobFactory; @Bean public Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setLocation(new ClassPathResource(\"/quartz.yml\")); propertiesFactoryBean.afterPropertiesSet(); return propertiesFactoryBean.getObject(); } @Bean public SchedulerFactoryBean schedulerFactoryBean() throws IOException { SchedulerFactoryBean factory = new SchedulerFactoryBean(); factory.setSchedulerName(\"Cluster_Scheduler\"); factory.setDataSource(dataSource); factory.setApplicationContextSchedulerContextKey(\"applicationContext\"); factory.setQuartzProperties(quartzProperties()); factory.setJobFactory(jobFactory); return factory; }} 2.2、quartz 任务激活失败在Quartz中,当一个持久化的触发器会因为: 调度器被关闭; 线程池没有可用线程; 项目重启; 任务的串行执行; 而错过激活时间,就会发生激活失败(misfire)。 可以设置 quartz中CornTrigger使用的策略12345678//所有的misfile任务马上执行public static final int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1;//在Trigger中默认选择MISFIRE_INSTRUCTION_FIRE_ONCE_NOW 策略public static final int MISFIRE_INSTRUCTION_SMART_POLICY = 0;// CornTrigger默认策略,合并部分misfire,正常执行下一个周期的任务。public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;//所有的misFire都不管,执行下一个周期的任务。public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2; 1、 通过setMisfireInstruction方法设置misfire策略。12345678CronTriggerFactoryBean triggerFactoryBean = new CronTriggerFactoryBean();triggerFactoryBean.setName(\"corn_\" + clazzName);triggerFactoryBean.setJobDetail(jobFactory.getObject());triggerFactoryBean.setCronExpression(quartzCorn);triggerFactoryBean.setGroup(QUARTZ_TRIGGER_GROUP);//设置misfire策略triggerFactoryBean.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY);triggerFactoryBean.afterPropertiesSet(); 2、 也可以通过CronScheduleBuilder设置misfire策略。1234567CronScheduleBuilder csb = CronScheduleBuilder.cronSchedule(\"0/5 * * * * ?\");//MISFIRE_INSTRUCTION_DO_NOTHING csb.withMisfireHandlingInstructionDoNothing();//MISFIRE_INSTRUCTION_FIRE_ONCE_NOWcsb.withMisfireHandlingInstructionFireAndProceed();//(默认)//MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICYcsb.withMisfireHandlingInstructionIgnoreMisfires(); 最后任务调度在实际项目中会经常用到,Quartz 也是我们的不二选择,但是在高可用的系统中也存在不少问题,具体问题小伙伴们可以在留言区留言,我们一起共同探讨。 参考与相关链接示例代码:https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-quartz Quartz 官网:http://www.quartz-scheduler.org/ Quartz集群原理及配置应用:https://www.cnblogs.com/xiang–liu/p/10120105.html 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}],"tags":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/tags/Spring-Boot/"}],"keywords":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}]},{"title":"SpringBoot-定时任务","slug":"210122-SpringBoot-定时任务","date":"2021-01-22T03:20:51.000Z","updated":"2021-01-26T03:15:40.671Z","comments":true,"path":"210122-SpringBoot-定时任务.html","link":"","permalink":"https://dddreams.github.io/210122-SpringBoot-定时任务.html","excerpt":"","text":"在我们开发项目中,定时任务是经常用到的一种技术,来处理一些业务,SpringBoot 默认支持定时任务,怎么样是不是感觉 Spring Boot 太人性化了,那么下面我们看一下怎么实现一个定时器吧。 Tips开发 Spring Boot 项目有个口诀或者说是 Spring Boot 的三板斧:加依赖、写配置、添注解示例代码详见:https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-schedule 加依赖Spring Boot 默认支持定时任务,所以只要加入 Spring Boot 的依赖即可1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId></dependency> 写配置实际开发中一般将经常变化的值配置到配置文件中,定时任务中我们可以自定义配置项 cron 来控制定时任务执行的时间1234567server: port: 8080spring: application: name: scheduleschedule: cron: 0 */1 * * * ? # 表达式表示 1 分钟执行一次 关于 cron 表达式,如果不清楚请先百度。 添注解我们需要在启动类上加 @EnableScheduling 注解即可开启定时任务1234567@SpringBootApplication@EnableSchedulingpublic class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }} 编写定时任务的代码123456789@Slf4j@Componentpublic class TestTasks { //@Scheduled(cron = \"0 */1 * * * ?\") @Scheduled(cron = \"${schedule.cron}\") public void testTask() { log.info(\"哈哈,我执行了!\"); }} 启动项目,便会在控制台隔1分钟打印1234哈哈,我执行了!哈哈,我执行了!哈哈,我执行了!... 注意: @Component 注解是启动后立即执行,${schedule.cron} 便是从配置文件读取配置的执行时间。 前面说了如果你对 cron 不了解,不用担心,Spring boot 提供了另外的方式 fixedRate,详解如下:123@Scheduled(fixedRate = 60000) :上一次开始执行时间点之后1分再执行@Scheduled(fixedDelay = 60000) :上一次执行完毕时间点之后1分再执行@Scheduled(initialDelay=1000, fixedRate=60000) :第一次延迟1秒后执行,之后按 fixedRate 的规则每1分执行一次 代码:1234@Scheduled(fixedRate = 60000)public void testTask1() { log.info(\"哈哈,我又执行了!\");} 结果:12345哈哈,我执行了!哈哈,我又执行了!哈哈,我执行了!哈哈,我又执行了!... 动态添加或删除定时任务在实际项目中,往往会遇到动态的添加或停止定时任务,来我们看看怎么实现,首先添加一个配置类1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980@Configurationpublic class DefaultSchedulingConfigurer implements SchedulingConfigurer { private ScheduledTaskRegistrar taskRegistrar; private Set<ScheduledFuture<?>> scheduledFutures = null; private Map<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<>(); @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { this.taskRegistrar = taskRegistrar; //System.out.println(inited()); } @SuppressWarnings(\"unchecked\") private Set<ScheduledFuture<?>> getScheduledFutures() { if (scheduledFutures == null) { try { // spring版本不同选用不同字段scheduledFutures scheduledFutures = (Set<ScheduledFuture<?>>) BeanUtils.getProperty(taskRegistrar, \"scheduledTasks\"); } catch (NoSuchFieldException e) { throw new SchedulingException(\"not found scheduledFutures field.\"); } } return scheduledFutures; } /** * 添加任务 */ public void addTriggerTask(String taskId, TriggerTask triggerTask) { if (taskFutures.containsKey(taskId)) { throw new SchedulingException(\"the taskId[\" + taskId + \"] was added.\"); } TaskScheduler scheduler = taskRegistrar.getScheduler(); ScheduledFuture<?> future = scheduler.schedule(triggerTask.getRunnable(), triggerTask.getTrigger()); getScheduledFutures().add(future); taskFutures.put(taskId, future); } /** * 取消任务 */ public void cancelTriggerTask(String taskId) { ScheduledFuture<?> future = taskFutures.get(taskId); if (future != null) { future.cancel(true); } taskFutures.remove(taskId); getScheduledFutures().remove(future); } /** * 重置任务 */ public void resetTriggerTask(String taskId, TriggerTask triggerTask) { cancelTriggerTask(taskId); addTriggerTask(taskId, triggerTask); } /** * 任务编号 */ public Set<String> taskIds() { return taskFutures.keySet(); } /** * 是否存在任务 */ public boolean hasTask(String taskId) { return this.taskFutures.containsKey(taskId); } /** * 任务调度是否已经初始化完成 */ public boolean inited() { return this.taskRegistrar != null && this.taskRegistrar.getScheduler() != null; }} 然后新建一个 controller12345678910111213141516171819@RestController@RequestMapping(\"/task\")public class TestTaskController { @Autowired private DefaultSchedulingConfigurer defaultSchedulingConfigurer; @GetMapping(\"/add\") public String add(@RequestParam(name = \"name\") String name) { defaultSchedulingConfigurer.addTriggerTask(name, new TriggerTask( () -> System.out.println(\"hello world!\"), new CronTrigger(\"0/5 * * * * ? \")));//5秒执行一次 return \"任务开启成功\"; } @GetMapping(\"/del\") public String del(@RequestParam(name = \"name\") String name) { defaultSchedulingConfigurer.cancelTriggerTask(name); return \"任务删除成功\"; }} 请求 http://localhost:8080/task/add,定时任务即可开启请求 http://localhost:8080/task/del,定时任务就删除成功了 最后其实 Spring boot 的定时任务相对比较简单,如果在高并发集群环境下,我们尽量使用框架来支撑我们的业务,下一节我会介绍定时任务的框架 quartz ,大家敬请期待吧。 参考与相关链接示例代码:https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-scheduleSpring boot 定时任务:http://www.ityouknow.com/springboot/2016/12/02/spring-boot-scheduler.htmlSpringBoot+schedule+可以动态添加或删除定时任务:https://blog.csdn.net/nicky_lc/article/details/106961779 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}],"tags":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/tags/Spring-Boot/"}],"keywords":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}]},{"title":"是年味淡了,还是我们老了","slug":"200121-是年味淡了,还是我们老了","date":"2020-01-21T06:29:11.000Z","updated":"2020-01-21T10:53:28.056Z","comments":true,"path":"200121-是年味淡了,还是我们老了.html","link":"","permalink":"https://dddreams.github.io/200121-是年味淡了,还是我们老了.html","excerpt":"","text":"对幸福说个早安,给吉祥送个花篮,给祝福送份快餐,祝大家春节快乐! 叮咚…微信收到一条消息。 「敲响的是钟声,走过的是岁月,留下的是故事,带来的是希望,盼望的是美好,送来的是祝福,愿朋友新年快乐。」是一条新年祝福,顺着华丽的符号,伴随着满屏的笑脸与礼花的表情雨,往上滑,发现和对方的上一次对话是在去年过年,我不禁陷入了沉思…「这人是谁啊!」。 “诶,有敬业福吗?来,我粘一张”,支付宝上集福集的热火朝天,每天一睁眼便浇水,一看到福字就扫,除夕夜里陪伴自己和家人最多的还是手机,终于等到开启五福了,结果,分到2.88元。 工作群里忽然热闹起来,各种祝福语,各种表情,令人紧张的是偶尔会有红包闪现,你紧盯屏幕,活动手指,生怕错过。终于来了,你庆幸自己眼疾手快抢到了,心情无比激动,感觉登上了人生巅峰,发现红包面额只有0.8元。 “狗娃啊,今年怎么没带媳妇回来啊!”,小时候盼望发压岁钱的七大姑八大姨变成了避而远之的催婚对象。你只能极其尴尬的,笑一声,“等着阿姨给介绍呢”。 突然感觉这个年,不像是小时候过的那个年了,感觉年味淡了,一切过年的活动都成了大公司散钱争用户的套路,朋友发来的新年祝福也显得那么苍白无力,只是一句虚话而已。那到底是年味淡了,还是我们老了? “三啊,你什么时候回来”,电话那头愣了半天,“哦,我明天就回来。”原本打算不回家的小儿子听到母亲的声音,决定回家过年,他急忙的打开12306网上购票,没想到票还有,没有往年那么紧张了。 “爷爷,我们已经在路上了,爸爸开的车”,老父亲开心嘱咐慢点开车,不用着急。挂断电话又急切的让母亲准备杀鱼杀鸡。 小两口刚生了二胎,今年不准备回家,而是把父母接过来,在城里过年。火车站上有的人迫切的回家,有的人焦急的等待。每出来一批人,他们都在人群中张望着,寻找着,终于看到了父母的身影,跑过去接住父亲手里提着的东西,开心的拥抱在一起。这几年来越来越多的人选择了“反向春运”,把老人孩子接到城里过年。 是的,“团圆”成了人们过年最大的愿望。小时候最开心的是吃到包着硬币的饺子,长大了,吃什么不重要,重要的是过年能跟家人在一起。 最后,提前祝大家春节快乐!年味未淡,我们不老! ps:这几天被一个叫做“新型肺炎”的病毒搞的人心惶惶,在这里也提醒读者朋友们注意防范,尽量避开人多的地方,加强自身卫生防止病毒传染。同时也保持镇定,不必恐慌,不要过于相信部分媒体扩大化的报道。 pps:估计大家已经看到了,我公众号改名字了,改名字的原因有二,第一是字母拼音不利于搜索;第二是博客感觉有点老气,所以改名为:「治恒说说」 本文同步更新至zhiheng’s blog、微信公众号。 微信搜一搜:治恒说说 记得点关注哦","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"Java1.8你还不知道的新特性","slug":"200110-Java1-8你还不知道的新特性","date":"2020-01-10T10:35:26.000Z","updated":"2020-01-10T10:37:04.855Z","comments":true,"path":"200110-Java1-8你还不知道的新特性.html","link":"","permalink":"https://dddreams.github.io/200110-Java1-8你还不知道的新特性.html","excerpt":"","text":"Lambda 表达式Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。 Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。 使用 Lambda 表达式可以使代码变的更加简洁紧凑。 12345678910111213141516171819202122String[] arr = {\"Google\", \"Baidu\", \"Taobao\", \"Sina\", \"Runoob\"};List<String> list = Arrays.asList(arr);// 使用 java 7 排序private void sortUsingJava7(List<String> names) { Collections.sort(names, new Comparator<String>() { @Override public int compare(String s1, String s2) { return s1.compareTo(s2); } });}// 使用 java 8 排序private void sortUsingJava8(List<String> names) { Collections.sort(names, (s1, s2) -> s1.compareTo(s2));}使用 Java 7 语法: [Baidu , Google , Runoob , Sina , Taobao ]使用 Java 8 语法: [Baidu , Google , Runoob , Sina , Taobao ] Stream流式操作问题:给定一个字符串元素列表,如下所示:1[\"1\", \"2\", \"bilibili\", \"of\", \"codesheep\", \"5\", \"at\", \"BILIBILI\", \"codesheep\", \"23\", \"CHEERS\", \"6\"] 找出所有长度 >=5 的字符串,并且忽略大小写、去除重复字符串,然后按字母排序,最后用“-”连接成一个字符串输出! 来,小伙伴们,5分钟,搞定它。。。什么搞定不了,该去补习补习了。那么使用Java8怎么搞定呢,开启装逼模式:12345678910String result = list.stream() // 首先将列表转化为Stream流 .filter( i -> !isNum(i) ) // 筛选出字母型字符串 .filter( i -> i.length() >= 5 ) // 其次筛选出长度>=5的字符串 .map( i -> i.toLowerCase() ) // 字符串统一转小写 .distinct() // 去重操作来一下 .sorted( Comparator.naturalOrder() ) // 字符串排序来一下 .collect( Collectors.joining(\"-\") ); // 连词成句来一下,完美!System.out.println(result);输出:bilibili-cheers-codesheep 这操作够sao了吧! Consumer接口Consumer的语义是消费的意思,了解一些消息队列的同学,肯定对这个单词,有一定的理解。看个简单的例子:打印3次 Hello World!123456789// 原来的写法System.out.println(\"Hello World!\");System.out.println(\"Hello World!\");System.out.println(\"Hello World!\");//使用Consumer接口Consumer c = System.out::println;c.andThen(c).andThen(c).accept(\"Hello World!\"); 搞定。 一个简单的平方计算:12Consumer<Integer> square = x -> System.out.println(\"print square : \" + x * x);square.accept(2); Optional接口Optional本质是个容器,你可以将你的变量交由它进行封装,这样我们就不用显式对原变量进行 null值检测,防止出现各种空指针异常。举例:123456789public Integer getScore( Student student ){ if(student != null){ Subject sub = student.getSubject(); if(sub != null){ return sub.getScore(); } } return null;} 多嵌套的 if 判空,这么处理当然可以,然而有更好的解决办法哦。123456public Integer getScore(Student student){ return Optional.ofNullable(student) .map(Student::getSubject) .map(Subject::getScore) .orElse(null);} 完美实现。 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"https://dddreams.github.io/tags/Java/"}],"keywords":[]},{"title":"java代码优化建议","slug":"200103-java代码优化建议","date":"2020-01-03T04:07:26.000Z","updated":"2020-01-03T05:08:50.624Z","comments":true,"path":"200103-java代码优化建议.html","link":"","permalink":"https://dddreams.github.io/200103-java代码优化建议.html","excerpt":"","text":"帮助开发人员夯实基础,从规范的编码操作开始,培养良好的编码习惯,助力未来技术成长。 通过java代码规范来优化程序,优化内存使用情况,防止内存泄露 可供程序利用的资源(内存、CPU时间、网络带宽等)是有限的,优化的目的就是让程序用尽可能少的资源完成预定的任务。优化通常包含两方面的内容:减小代码的体积,提高代码的运行效率。本文讨论的主要是如何提高代码的效率。在Java程序中,性能问题的大部分原因并不在于Java语言,而是在于程序本身。养成好的代码编写习惯非常重要,比如正确地、巧妙地运用java.lang.String类和java.util.Vector类,它能够显著地提高程序的性能。下面我们就来具体地分析一下这方面的问题。 1、 尽量指定类的final修饰符 带有final修饰符的类是不可派生的。 在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50% 。 2、 尽量重用对象。 特别是String 对象的使用中,出现字符串连接情况时应用StringBuffer 代替。由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理。因此,生成过多的对象将会给程序的性能带来很大的影响。 3、 尽量使用局部变量,调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。 其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。另外,依赖于具体的编译器/JVM,局部变量还可能得到进一步优化。请参见《尽可能使用堆栈变量》。 4、 不要重复初始化变量 默认情况下,调用类的构造函数时, Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。 5、 在JAVA + ORACLE 的应用系统开发中,java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。 6、 Java 编程过程中,进行数据库连接、I/O流操作时务必小心,在使用完毕后,即使关闭以释放资源。 因为对这些大对象的操作会造成系统大的开销,稍有不慎,会导致严重的后果。 7、 由于JVM的有其自身的GC机制,不需要程序开发者的过多考虑,从一定程度上减轻了开发者负担,但同时也遗漏了隐患,过分的创建对象会消耗系统的大量内存,严重时会导致内存泄露,因此,保证过期对象的及时回收具有重要意义。 JVM回收垃圾的条件是:对象不在被引用;然而,JVM的GC并非十分的机智,即使对象满足了垃圾回收的条件也不一定会被立即回收。所以,建议我们在对象使用完毕,应手动置成null。 8、 在使用同步机制时,应尽量使用方法同步代替代码块同步。 9、 尽量减少对变量的重复计算1234567for(int i = 0;i < list.size; i ++) {…}应替换为:for(int i = 0,int len = list.size();i < len; i ++){…} 10、尽量采用lazy loading 的策略,即在需要的时候才开始创建。 12345678910例如: String str = “aaa”;if(i == 1) { list.add(str);}应替换为:if(i == 1) { String str = “aaa”; list.add(str);} 11、慎用异常 异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。 异常只能用于错误处理,不应该用来控制程序流程。 12、不要在循环中使用: 12345Try {} catch() {} 应把其放置在最外层。 13、StringBuffer 的使用: StringBuffer表示了可变的、可写的字符串。有三个构造方法 :StringBuffer (); //默认分配16个字符的空间StringBuffer (int size); //分配size个字符的空间StringBuffer (String str); //分配16个字符+str.length()个字符空间你可以通过StringBuffer的构造函数来设定它的初始化容量,这样可以明显地提升性能。 这里提到的构造函数是StringBuffer(int length),length参数表示当前的StringBuffer能保持的字符数量。你也可以使用ensureCapacity(int minimumcapacity)方法在StringBuffer对象创建之后设置它的容量。首先我们看看StringBuffer的缺省行为,然后再找出一条更好的提升性能的途径。StringBuffer在内部维护一个字符数组,当你使用缺省的构造函数来创建StringBuffer对象的时候,因为没有设置初始化字符长度,StringBuffer的容量被初始化为16个字符,也就是说缺省容量就是16个字符。当StringBuffer达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,也就是(2旧值+2)。如果你使用缺省值,初始化之后接着往里面追加字符,在你追加到第16个字符的时候它会将容量增加到34(216+2),当追加到34个字符的时候就会将容量增加到70(2*34+2)。无论何事只要StringBuffer到达它的最大容量它就不得不创建一个新的字符数组然后重新将旧字符和新字符都拷贝一遍――这也太昂贵了点。所以总是给StringBuffer设置一个合理的初始化容量值是错不了的,这样会带来立竿见影的性能增益。StringBuffer初始化过程的调整的作用由此可见一斑。所以,使用一个合适的容量值来初始化StringBuffer永远都是一个最佳的建议。 14、合理的使用Java类 java.util.Vector。 简单地说,一个Vector就是一个java.lang.Object实例的数组。Vector与数组相似,它的元素可以通过整数形式的索引访问。但是,Vector类型的对象在创建之后,对象的大小能够根据元素的增加或者删除而扩展、缩小。请考虑下面这个向Vector加入元素的例子:12345Object bj = new Object();Vector v = new Vector(100000);for(int i = 0; i < 100000; i++) { v.add(0,obj); } 除非有绝对充足的理由要求每次都把新元素插入到Vector的前面,否则上面的代码对性能不利。在默认构造函数中,Vector的初始存储能力是10个元素,如果新元素加入时存储能力不足,则以后存储能力每次加倍。Vector类就对象StringBuffer类一样,每次扩展存储能力时,所有现有的元素都要复制到新的存储空间之中。下面的代码片段要比前面的例子快几个数量级:12345Object bj = new Object();Vector v = new Vector(100000);for(int i = 0; i < 100000; i++) { v.add(obj); } 同样的规则也适用于Vector类的remove()方法。由于Vector中各个元素之间不能含有“空隙”,删除除最后一个元素之外的任意其他元素都导致被删除元素之后的元素向前移动。也就是说,从Vector删除最后一个元素要比删除第一个元素“开销”低好几倍。 假设要从前面的Vector删除所有元素,我们可以使用这种代码:123for(int i = 0; i < 100000; i++) { v.remove(0);} 但是,与下面的代码相比,前面的代码要慢几个数量级:123for(int i = 0; i < 100000; i++) { v.remove(v.size()-1);} 从Vector类型的对象v删除所有元素的最好方法是:1v.removeAllElements(); 假设Vector类型的对象v包含字符串“Hello”。考虑下面的代码,它要从这个Vector中删除“Hello”字符串:123String s = \"Hello\";int i = v.indexOf(s);if(I != -1) v.remove(s); 这些代码看起来没什么错误,但它同样对性能不利。在这段代码中,indexOf()方法对v进行顺序搜索寻找字符串“Hello”,remove(s)方法也要进行同样的顺序搜索。改进之后的版本是:123String s = \"Hello\";int i = v.indexOf(s);if(I != -1) v.remove(i); 这个版本中我们直接在remove()方法中给出待删除元素的精确索引位置,从而避免了第二次搜索。一个更好的版本是:12String s = \"Hello\"; v.remove(s); 最后,我们再来看一个有关Vector类的代码片段:1for(int i = 0; i++;i < v.length) 如果v包含100,000个元素,这个代码片段将调用v.size()方法100,000次。虽然size方法是一个简单的方法,但它仍旧需要一次方法调用的开销,至少JVM需要为它配置以及清除堆栈环境。在这里,for循环内部的代码不会以任何方式修改Vector类型对象v的大小,因此上面的代码最好改写成下面这种形式:12int size = v.size(); for(int I=0; I++;I<size) 虽然这是一个简单的改动,但它仍旧赢得了性能。毕竟,每一个CPU周期都是宝贵的。 15、当复制大量数据时,使用System.arraycopy()命令。12345678int[] src={1,3,5,6,7,8};int[] dest = new int[6];System.arraycopy(src, 0, dest, 0, 6);src:源数组; srcPos:源数组要复制的起始位置;dest:目的数组; destPos:目的数组放置的起始位置;length:复制的长度.注意:src and dest都必须是同类型或者可以进行转换类型的数组. 16、代码重构:增强代码的可读性。12345678910111213141516171819202122public class ShopCart { private List carts ; … public void add (Object item) { if(carts == null) { carts = new ArrayList(); } crts.add(item); } public void remove(Object item) { if(carts. contains(item)) { carts.remove(item); } } public List getCarts() { //返回只读列表 return Collections.unmodifiableList(carts); } //不推荐这种方式 //this.getCarts().add(item);} 17、不用new关键词创建类的实例 用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现:123public static Credit getNewCredit() { return new Credit();} 改进后的代码使用clone()方法,如下所示:1234private static Credit BaseCredit = new Credit(); public static Credit getNewCredit() { return (Credit) BaseCredit.clone();} 上面的思路对于数组处理同样很有用。 18、乘法和除法 考虑下面的代码:123for (val = 0; val < 100000; val +=5) { alterX = val * 8; myResult = val * 2;} 用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码:123for (val = 0; val < 100000; val += 5) { alterX = val << 3; myResult = val << 1;} 修改后的代码不再做乘以8的操作,而是改用等价的左移3位操作,每左移1位相当于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。 19、在JSP页面中关闭无用的会话。 一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。对于那些无需跟踪会话状态的页面,关闭自动创建的会话可以节省一些资源。使用如下page指令:1<%@ page session=\"false\"%> 20、JDBC与I/O 如果应用程序需要访问一个规模很大的数据集,则应当考虑使用块提取方式。默认情况下,JDBC每次提取32行数据。举例来说,假设我们要遍历一个5000行的记录集,JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512,则调用数据库的次数将减少到10次。 21、Servlet与内存使用 许多开发者随意地把大量信息保存到用户会话之中。一些时候,保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看,典型的症状是用户感到系统周期性地变慢,却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间,它的表现是内存占用不正常地大起大落。解决这类内存问题主要有二种办法。第一种办法是,在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样,只要实现valueUnbound()方法,就可以显式地释放Bean使用的资源。 另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外,也可以用编程的方式调用会话的setMaxInactiveInterval()方法,该方法用来设定在作废会话之前,Servlet容器允许的客户请求的最大间隔时间,以秒计。 22、使用缓冲标记 一些应用服务器加入了面向JSP的缓冲标记功能。例如,BEA的WebLogic Server从6.0版本开始支持这个功能,Open Symphony工程也同样支持这个功能。JSP缓冲标记既能够缓冲页面片断,也能够缓冲整个页面。当JSP页面执行时,如果目标片断已经在缓冲之中,则生成该片断的代码就不用再执行。页面级缓冲捕获对指定URL的请求,并缓冲整个结果页面。对于购物篮、目录以及门户网站的主页来说,这个功能极其有用。对于这类应用,页面级缓冲能够保存页面执行的结果,供后继请求使用。 23、选择合适的引用机制 在典型的JSP应用系统中,页头、页脚部分往往被抽取出来,然后根据需要引入页头、页脚。当前,在JSP页面中引入外部资源的方法主要有两种:include指令,以及include动作。include指令:例如<%@ include file=”copyright.html” %>。该指令在编译时引入指定的资源。在编译之前,带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定,比运行时才确定资源更高效。include动作:例如<jsp:include page="copyright.jsp" />。该动作引入指定页面执行后生成的结果。由于它在运行时完成,因此对输出结果的控制更加灵活。但时,只有当被引用的内容频繁地改变时,或者在对主页面的请求没有出现之前,被引用的页面无法确定时,使用include动作才合算。 24、及时清除不再需要的会话 为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为30分钟。当应用服务器需要保存更多会话时,如果内存容量不足,操作系统会把部分内存数据转移到磁盘,应用服务器也可能根据“最近最频繁使用”(Most Recently Used)算法把部分不活跃的会话转储到磁盘,甚至可能抛出“内存不足”异常。在大规模系统中,串行化会话的代价是很昂贵的。当会话不再需要时,应当及时调用HttpSession.invalidate()方法清除会话。HttpSession.invalidate()方法通常可以在应用的退出页面调用。 25、不要将数组声明为:public static final 。 26、HashMap的遍历效率讨论 经常遇到对HashMap中的key和value值对的遍历操作,有如下两种方法:1234567891011121314Map<String, String[]> paraMap = new HashMap<String, String[]>();//第一个循环Set<String> appFieldDefIds = paraMap.keySet();for (String appFieldDefId : appFieldDefIds) { String[] values = paraMap.get(appFieldDefId); ......}//第二个循环for(Entry<String, String[]> entry : paraMap.entrySet()){ String appFieldDefId = entry.getKey(); String[] values = entry.getValue(); .......} 第一种实现明显的效率不如第二种实现。分析如下 Set<String> appFieldDefIds = paraMap.keySet(); 是先从HashMap中取得keySet代码如下:12345678910111213141516171819202122public Set<K> keySet() { Set<K> ks = keySet; return (ks != null ? ks : (keySet = new KeySet()));}private class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return newKeyIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { return HashMap.this.removeEntryForKey(o) != null; } public void clear() { HashMap.this.clear(); }} 其实就是返回一个私有类KeySet, 它是从AbstractSet继承而来,实现了Set接口。 再来看看for/in循环的语法12for(declaration : expression)statement 在执行阶段被翻译成如下各式1234for(Iterator<E> #i = (expression).iterator(); #i.hashNext();){ declaration = #i.next(); statement} 因此在第一个for语句for (String appFieldDefId : appFieldDefIds)中调用了HashMap.keySet().iterator() 而这个方法调用了newKeyIterator()12345678Iterator<K> newKeyIterator() { return new KeyIterator();}private class KeyIterator extends HashIterator<K> { public K next() { return nextEntry().getKey(); }} 所以在for中还是调用了在第二个循环for(Entry<String, String[]> entry : paraMap.entrySet())中使用的Iterator是如下的一个内部类12345private class EntryIterator extends HashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); }} 此时第一个循环得到key,第二个循环得到HashMap的Entry效率就是从循环里面体现出来的第二个循环此致可以直接取key和value值而第一个循环还是得再利用HashMap的get(Object key)来取value值现在看看HashMap的get(Object key)方法12345678910111213public V get(Object key) { Object k = maskNull(key); int hash = hash(k); int i = indexFor(hash, table.length); //Entry[] table Entry<K,V> e = table; while (true) { if (e == null) return null; if (e.hash == hash && eq(k, e.key)) return e.value; e = e.next; }} 其实就是再次利用Hash值取出相应的Entry做比较得到结果,所以使用第一中循环相当于两次进入HashMap的Entry 中而第二个循环取得Entry的值之后直接取key和value,效率比第一个循环高。其实按照Map的概念来看也应该是用第二个循环好一点,它本来就是key和value的值对,将key和value分开操作在这里不是个好选择。 27、array(数组) 和 ArryList的使用 array([]):最高效;但是其容量固定且无法动态改变;ArrayList:容量可动态增长;但牺牲效率;基于效率和类型检验,应尽可能使用array,无法确定数组大小时才使用ArrayList!ArrayList是Array的复杂版本ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。ArrayList存入对象时,抛弃类型信息,所有对象屏蔽为Object,编译时不检查类型,但是运行时会报错。注:jdk5中加入了对泛型的支持,已经可以在使用ArrayList时进行类型检查。从这一点上看来,ArrayList与数组的区别主要就是由于动态增容的效率问题了 28、尽量使用HashMap 和ArrayList ,除非必要,否则不推荐使用HashTable和Vector ,后者由于使用同步机制,而导致了性能的开销。 29、StringBuffer 和StringBuilder的区别: java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。 StringBuilder。与该类相比,通常应该优先使用 java.lang.StringBuilder类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。为了获得更好的性能,在构造 StirngBuffer 或 StirngBuilder 时应尽可能指定它的容量。当然,如果你操作的字符串长度不超过 16 个字符就不用了。 相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%-15% 左右的性能提升,但却要冒多线程不安全的风险。而在现实的模块化编程中,负责某一模块的程序员不一定能清晰地判断该模块是否会放入多线程的环境中运行,因此:除非你能确定你的系统的瓶颈是在 StringBuffer 上,并且确定你的模块不会运行在多线程模式下,否则还是用 StringBuffer 吧。 30、尽量避免使用split 除非是必须的,否则应该避免使用split,split由于支持正则表达式,所以效率比较低,如果是频繁的几十,几百万的调用将会耗费大量资源,如果确实需要频繁的调用split,可以考虑使用apache的 StringUtils.split(string,char),频繁split的可以缓存结果。 其他补充: 1、及时清除不再使用的对象,设为null2、尽可能使用final,static等关键字3、尽可能使用buffered对象 如何优化代码使JAVA源文件及编译后CLASS文件更小 1 尽量使用继承,继承的方法越多,你要写的代码量也就越少 2 打开JAVA编译器的优化选项: javac -O 这个选项将删除掉CLASS文件中的行号,并能把一些private, static,final的小段方法申明为inline方法调用 3 把公用的代码提取出来 4 不要初始化很大的数组,尽管初始化一个数组在JAVA代码中只是一行的代码量,但编译后的代码是一行代码插入一个数组的元素,所以如果你有大量的数据需要存在数组中的话,可以先把这些数据放在String中,然后在运行期把字符串解析到数组中 5 日期类型的对象会占用很大的空间,如果你要存储大量的日期对象,可以考虑把它存储为long型,然后在使用的时候转换为Date类型 6 类名,方法名和变量名尽量使用简短的名字,可以考虑使用Hashjava, Jobe, Obfuscate and Jshrink等工具自动完成这个工作 7 将static final类型的变量定义到Interface中去 8 算术运算 能用左移/右移的运算就不要用*和/运算,相同的运算不要运算多次 2、不要两次初始化变量Java通过调用独特的类构造器默认地初始化变量为一个已知的值。所有的对象被设置成null,integers (byte, short, int, long)被设置成0,float和double设置成0.0,Boolean变量设置成false。这对那些扩展自其它类的类尤其重要,这跟使用一个新的关键词创建一个对象时所有一连串的构造器被自动调用一样。 3、在任何可能的地方让类为Final标记为final的类不能被扩展。在《核心Java API》中有大量这个技术的例子,诸如java.lang.String。将String类标记为final阻止了开发者创建他们自己实现的长度方法。更深入点说,如果类是final的,所有类的方法也是final的。Java编译器可能会内联所有的方法(这依赖于编译器的实现)。在我的测试里,我已经看到性能平均增加了50%。 3、异常在需要抛出的地方抛出,try catch能整合就整合12345678910111213141516try { some.method1(); // Difficult for javac} catch( method1Exception e ) { // and the JVM runtime // Handle exception 1 // to optimize this} // codetry { some.method2();} catch( method2Exception e ) { // Handle exception 2}try { some.method3();} catch( method3Exception e ) { // Handle exception 3} 已下代码 更容易被编译器优化1234567891011try { some.method1(); // Easier to optimize some.method2(); some.method3();} catch( method1Exception e ) { // Handle exception 1} catch( method2Exception e ) { // Handle exception 2} catch( method3Exception e ) { // Handle exception 3} 4、For循环的优化 123456789Replace…for( int i = 0; i < collection.size(); i++ ) {...}with…for( int i = 0, n = collection.size(); i < n; i++ ) {...} 5、 在JAVA + ORACLE 的应用系统开发中,java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。 6、尽量采用lazy loading 的策略,即在需要的时候才开始创建。123456789例如: String str = “aaa”;if(i == 1) { list.add(str);}应替换为:if(i == 1) { String str = “aaa”; list.add(str);} 7、不要在循环中使用:12345Try {} catch() {} 应把其放置在最外层 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"https://dddreams.github.io/tags/Java/"}],"keywords":[]},{"title":"SpringBoot-Thymeleaf模版引擎","slug":"180718-Thymeleaf模版引擎","date":"2019-01-04T10:07:00.000Z","updated":"2020-01-03T05:09:15.938Z","comments":true,"path":"180718-Thymeleaf模版引擎.html","link":"","permalink":"https://dddreams.github.io/180718-Thymeleaf模版引擎.html","excerpt":"","text":"上一节我们简单的认识了 Thymeleaf,并简单介绍了它与 Spring Boot 的结合,这一节来具体的看一下,Thymeleaf 是的语法和应用。 创建模版文件创建文件其实在上节中已经说过,这边在重复一遍,新建 HTML 文件,在头文件中添加 xmlns:th="http://www.thymeleaf.org"1<!DOCTYPE html><html xmlns:th=\"http://www.thymeleaf.org\"></html> 注意:html 中的标签必须严格规范,标签必须闭合,即<div />技术或者</div>类似结束。 表达式语法它们分为四类: 1.变量表达式 2.选择或星号表达式 3.通用 Message 表达式 4.URL表达式 变量表达式ongl标准语法或者 Spring EL 表达式 ${user.userName},它们将以 html 标签的一个属性来表示 选择或星号表达式123456789101112131415161718<div th:object=\"${user}\"> <p>Name: <span th:text=\"*{firstName}\">Sebastian</span>.</p> <p>Surname: <span th:text=\"*{lastName}\">Pepper</span>.</p> <p>Nationality: <span th:text=*{nationality}\">Saturn</span>.</p></div> 等价于<div> <p>Name: <span th:text=\"${user.firstName}\">Sebastian</span>.</p> <p>Surname: <span th:text=\"${user.lastName}\">Pepper</span>.</p> <p>Nationality: <span th:text=\"${user.nationality}\">Saturn</span>.</p></div>当然了,这两者可以混合使用还有一种方式<div> <p>Name: <span th:text=\"*{user.name}\">Sebastian</span>.</p> <p>Surname: <span th:text=\"*{user.surname}\">Pepper</span>.</p> <p>Nationality: <span th:text=\"*{user.nationality}\">Saturn</span>.</p></div> 通用 Message 表达式通用表达式允许我们从外部的配置文件(properties)中取值,用 Key,Value的形式。12#{main.title} #{message.entrycreated(${entryId})} 可以在模板文件中找到这样的表达式代码:1234<table> <th th:text=\"#{header.address.city}\">...</th> <th th:text=\"#{header.address.country}\">...</th> </table> URL表达式URL表达式指的是把一个有用的上下文或回话信息添加到URL,这个过程经常被叫做URL重写。 @{/order/list}URL还可以设置参数: @{/order/details(id=${orderId})}相对路径: @{../documents/report} 表达式支持的语法字面(Literals) 文本文字(Text literals): ‘one text’, ‘Another one!’,… 数字文本(Number literals): 0, 34, 3.0, 12.3,… 布尔文本(Boolean literals): true, false 空(Null literal): null 文字标记(Literal tokens): one, sometext, main,… 文本操作(Text operations) 字符串连接(String concatenation): + 文本替换(Literal substitutions): |The name is ${name}| 算术运算(Arithmetic operations) 二元运算符(Binary operators): +, -, *, /, % 减号(单目运算符)Minus sign (unary operator): - 布尔操作(Boolean operations) 二元运算符(Binary operators):and, or 布尔否定(一元运算符)Boolean negation (unary operator):!, not 比较和等价(Comparisons and equality) 比较(Comparators): >, <, >=, <= (gt, lt, ge, le) 等值运算符(Equality operators):==, != (eq, ne) 条件运算符(Conditional operators) If-then: (if) ? (then) If-then-else: (if) ? (then) : (else) Default: (value) ?: (defaultvalue) th 标签参考 thymeleaf 官方文档:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf 常用的使用方法相关参考和链接Thymeleaf 模板的使用:http://www.cnblogs.com/lazio10000/p/5603955.html 新一代Java模板引擎Thymeleaf:https://www.tianmaying.com/tutorial/using-thymeleaf 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/tags/Spring-Boot/"}],"keywords":[]},{"title":"SpringBoot-单元测试及Thymeleaf模版","slug":"180716-SpringBoot-单元测试及Thymeleaf模版","date":"2018-08-13T03:20:51.000Z","updated":"2021-01-26T03:16:00.702Z","comments":true,"path":"180716-SpringBoot-单元测试及Thymeleaf模版.html","link":"","permalink":"https://dddreams.github.io/180716-SpringBoot-单元测试及Thymeleaf模版.html","excerpt":"","text":"上一节中我们已经做了一个简单的 Hello world!应用,这一节我们来学习 Spring Boot 的 web 开发。以前做过 web 的童鞋都知道,一个 web 应用最常用的就是 MVC 的模式 jsp(视图层),Servlet(控制层),Dao(数据持久层),另外还会用到 单元测试,Json,Filte,Property,Log,数据库操作,热部署等一些相关的技术。这节我们就来看看在 Spring Boot 中是如何使用这些技术的。 单元测试我们来为上节中的 hello world!应用进行单元测试。Spring Boot 的单元测试推荐使用 mockmvc 来进行,好处是不用启动服务即可进行测试。首先添加 test 的 maven 依赖, test 的依赖默认是存在的。 12345<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency> 在 src/test/ 下面新建包 controller ,然后新建类 HelloTest.java。 1234567891011121314151617@RunWith(SpringRunner.class)@SpringBootTestpublic class HelloTest { private MockMvc mvc; @Before public void setUp() throws Exception { mvc = MockMvcBuilders.standaloneSetup(new HelloController()).build(); } @Test public void getHello() throws Exception { mvc.perform(MockMvcRequestBuilders.get(\"/\").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(equalTo(\"hello world!\"))); }} 运行测试方法。 Jsp Or ThymeleafSpring Boot 推荐使用 Thymeleaf 来代替 Jsp,那么我们先来认识一下 Thymeleaf 是个什么东东 Thymeleaf 是个什么?Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点: Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。 Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。 Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。 如何应用 同样的首先添加 Thymeleaf 的 maven 组件依赖,这就是 Spring Boot 的优点,把所有优秀的组件都集成进来,使用时仅仅添加依赖就可以了。 1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency> spring-boot-starter-thymeleaf 组件中已经包含了 web 的组件,也可以将 spring-boot-starter-web 依赖删除。 然后在 resources/templates/ 下新建 hello.html 注意添加头文件 xmlns:th="http://www.thymeleaf.org" 123<!DOCTYPE html><html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\"><head> ... </head> 修改 HelloController 与 hello.html 123456789@Controllerpublic class HelloController { @RequestMapping(\"/hello\") public String hello(Model model, @RequestParam(value=\"name\", required=false, defaultValue=\"World\") String name){ model.addAttribute(\"name\", name); return \"hello\"; }} 12345678910<!DOCTYPE html><html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\"><head> <meta charset=\"UTF-8\"> <title>Spring Boot</title></head><body><p th:text=\"'Hello, ' + ${name} + '!'\" /></body></html> 启动服务,访问 http://localhost:8080/hello?name=Shure 参考与相关链接纯洁的微笑博客:http://www.ityouknow.com/springboot/2016/01/06/spring-boot-quick-start.html thymeleaf参考手册 by CSDN:https://blog.csdn.net/zrk1000/article/details/72667478 示例代码:https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-helloWorld 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}],"tags":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/tags/Spring-Boot/"}],"keywords":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}]},{"title":"给“抽奖助手”的一点点建议","slug":"180717-给抽奖助手的一点点建议","date":"2018-07-17T10:02:32.000Z","updated":"2018-07-17T10:14:34.907Z","comments":true,"path":"180717-给抽奖助手的一点点建议.html","link":"","permalink":"https://dddreams.github.io/180717-给抽奖助手的一点点建议.html","excerpt":"","text":"“抽奖助手”是无码科技做的一款小程序,如果你不知道无码科技,冯大辉你一定听说过吧,“抽奖助手”就是他家的产品,当然如果你听说过冯大辉那么你一定知道他的言论是有多么的犀利,尤其是对互联网产品,吐槽起来一针见血见解独到,好多公司都不敢让他来测评自家的产品,怕被爆出很多问题来,今天我也要给他家的产品“抽奖助手”提一点建议。 之前在冯大的公众号上看过一篇关于推荐系统和用户画像的文章,标题是什么我忘了,我觉得那篇文章写的很好,也是收益与那篇文章的启发,提出这点建议。 首先要感谢“抽奖助手”昨天我中奖了,很激动,我参与抽奖460多次,这是第一次中奖,然而我仔细一看中奖的内容却是“婴儿屁屁湿巾”,我激动的心情一下就没了,我一个还没结婚的人,让我中这么个奖,你说让我领呢还是不领呢? 所以我的建议是,通过用户画像分析出参与抽奖用户的基本情况,筛选出一些真正有需要该奖品的用户,让其中奖,这样让中奖者不再有心理上的落差。比如说,我是一个程序员,肯定对电子产品有浓厚的兴趣,如果奖品是个机械键盘什么的,可以优先让像我这样有兴趣的一类用户中奖,而不是在家带孩子的宝妈;再比如,奖品是婴儿用品,通过对参与抽奖的用户的分析,优先让拥有宝妈、已婚、刚结婚等属性的用户中奖,而不是像我这样还没结婚的用户,这样是不是更接近完美了呢。 可能有人会反驳了,你看到婴儿用品可以不参与啊,为什么你不想要,还要参与抽奖呢?其实,在“抽奖助手”里有很多人是不看奖品是什么的,直接划到最后点击“参与抽奖”,要知道一个奖项参与的用户好几十万,如果设定只有一人中奖,那么中奖的概率几十万分之一,很多人已经不报有中奖的希望,只是重在参与而已。所以这种反驳个人认为已经可以排除在外了。 也许这就是作为一个程序员对产品的一点点思考吧,下面推荐一个抽奖,扫码参与,或许会中奖哦! 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"SpringBoot-入坑SpringBoot","slug":"180710-入坑SpringBoot","date":"2018-07-11T03:43:32.000Z","updated":"2021-01-26T03:15:50.312Z","comments":true,"path":"180710-入坑SpringBoot.html","link":"","permalink":"https://dddreams.github.io/180710-入坑SpringBoot.html","excerpt":"","text":"做 Java 开发的小伙伴无人不知,无人不晓的 Spring ,一个叫做春的框架。以前的我们对 Spring 是既爱又恨,为什么呢,爱是因为它的便捷,恨是因为它的繁琐(这本来就是一个矛盾),后来 Spring Boot 诞生了,妈妈再也不用担心人们会恨它了。 什么是 Spring BootSpring Boot 是一个全新的框架,准确的说它不算是一个框架,它集成了所有常用框架,通过特定的方式配置,简化了基于 Spring 的应用开发,通过少量的代码就能创建一个独立的、产品级別的 Spring 应用,可以说它是一个框架的集合。 Spring Boot 的优点想想我们在使用 SpringMVC 的时候,搭建一个项目要写(web.xml,applicationContext.xml,dispatcher-servlet.xml等等等)这么多的 xml 配置文件,要有数据库连接配置,spring 事务配置,日志文件配置等一大堆配置后部署到 tomcat 运行,而且如果是一个简单的应用程序,例如发送一份邮件,也需要编写这么多的配置。 然而用 Spring Boot 就很简单了,几个简单的配置就能搭建出一套 web 服务或者微服务,Spring Boot 非常易于微服务的开发和 RESTful 接口开发。它的一大优点是提高开发者的生产力,因为它已经提供了许多通用的功能,很容易进行装配及使用。 创建第一个 Spring Boot 应用Spring Boot 能够无缝地为基于Maven和Gradle的项目提供各种构建工具,接下来我们就用 Maven 构建第一个应用。打开 http://start.spring.io/ 选择 Maven Project 使用 Java 语言,然后填写应用的基本信息,点击 Generate Project 就会下载下来,解压之后导入你的 IDE ,初始化的项目就长这个样子 然后引入 web 模块,编写 pom.xml, 添加包依赖 1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> pom.xml 文件中默认的有两个模块spring-boot-starter :核心模块,包括自动配置支持、日志和YAML;spring-boot-starter-test :测试模块,包括JUnit、Hamcrest、Mockito。 Hello World新建 controller 包,新建 HelloController 类 1234567891011import ...@RestControllerpublic class HelloController { @RequestMapping(\"/\") public String hello(){ return \"hello world!\"; }} @RestController 的意思就是controller里面的方法都以json格式输出,不用再写什么jackjson配置的了! 启动应用程序,在后台会看到 Spring 的字样说明已经启动成功,打开浏览器地址栏输入 localhost:8080 ,出现 hello world! ,这样我们的第一个 Spring Boot 应用程序就好了。 参考与相关链接Spring Boot 官网:http://spring.io/projects/spring-boot 纯洁的微笑博客:http://www.ityouknow.com/spring-boot.html 示例代码:https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-helloWorld 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}],"tags":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/tags/Spring-Boot/"}],"keywords":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"https://dddreams.github.io/categories/Spring-Boot/"}]},{"title":"关于个人隐私泄露,你怎么看?","slug":"180331-关于个人隐私泄露,你怎么看?","date":"2018-03-31T01:03:32.000Z","updated":"2018-04-21T07:20:39.835Z","comments":true,"path":"180331-关于个人隐私泄露,你怎么看?.html","link":"","permalink":"https://dddreams.github.io/180331-关于个人隐私泄露,你怎么看?.html","excerpt":"","text":"我们的个人隐私数据是怎么泄露的? 去年的时候公司加入了一项健康指标考核,各团队在企业咕咚里创建了运动团队,以每个月走的步数考核员工健康,我们团队制定了每月 20 万步的指标,规定一出大家都想方设法的刷步数,有的用手摇,有的抖腿,还有的直接买了刷步神器。而在前几天咕咚突然要收费了,而且费用还不少,公司不愿花钱,所以找可以替代的应用,听同事们说有一款叫做「钉钉」的企业应用,用的人挺多,于是就下载注册体验一下,注册之后,提示获取各种权限,地理位置,消息推送通知,网络,还有通讯录,我一贯的做法是除了网络,其他都不允许,但是进入应用之后发现已经有两位朋友了,而且还是我的手机通讯录的朋友,这让我挺纳闷的,仔细一想,应该是朋友注册的时候允许通讯录的权限了,一下子对这个应用好感全无;大概浏览了几页,真不愧是跟某宝出自一家啊,上面应有尽有,你所想不到的都做上去了,然而就是没有运动计步的。 小的时候,大人们总爱看孩子的隐私,撬你上了锁的日记本,偷看你的手机短信,聊天记录,我们感觉很气愤。大人们却不这么想,“你有什么不能让我看的,不能看就是有鬼!”,我们盼望这长大,长大后就可以保护我们的隐私和心事了。 然而长大后,在这个网络时代,我们近似于裸奔。你在网上点到了贷款的广告,接下来的几天里就接到数个贷款推销的电话;你下载了一个拍照 APP ,发现它索要联系人权限,你拒绝了,然后这个 APP 就不能用了;你在某宝软件上想看一下去年的消费情况,结果发现自己不知不觉又把权限开放出去了。 前几天李彦宏在中国发展高层论坛的一次对话中说“中国人对隐私问题的态度更加开放,相对来说也没有那么敏感。如果可以用隐私换取利益、安全、或者效率,在很多情况下他们是愿意这么做的”。这段话迅速上了各大媒体的新闻标题,并遭到了网民的炮轰。然而事件过后细想时,发现李彦宏只不过是说了句大实话而已,现在的国人不就是这样吗?朋友圈里每天领优惠券,领红包的广告没少见吧,但是打开都会让你注册或者填写手机号码领取,又有多少人放弃了领红包的想法而拒绝填写自己的手机号呢? 再前几天 Facebook 数据泄露,股价暴跌,6个交易日市值缩掉了一个百度。如果这件事发生在中国,那都不算事,谁在乎那些数据呢? 我们的个人数据是怎么泄露的,是我们为了领几块钱的红包拱手相让的,还是那些流氓企业在无声无息中偷偷获取的。互联网时代,我们都忙着抢红包薅羊毛了;下一个时代,人工智能时代,隐私对我们来说还是那么无所谓么? 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"欢迎入坑小程序","slug":"180326-欢迎入坑小程序","date":"2018-03-26T01:03:32.000Z","updated":"2018-04-21T07:24:57.309Z","comments":true,"path":"180326-欢迎入坑小程序.html","link":"","permalink":"https://dddreams.github.io/180326-欢迎入坑小程序.html","excerpt":"","text":"小马哥家的微信,微信家的小程序,来吧欢迎入坑小程序 今天的公众号圈里各大佬都在推荐一门课程「9小时搞定微信小程序开发」,于是乎我也入坑了小程序,这样显得跟大佬们一样肩并肩了,哈哈 关于小程序就不在介绍什么了,今年年初的「跳一跳」相信大家都玩的不亦乐乎,很显然小程序已经走上了一个新的舞台。昨天在深圳举办的第 10 届2018中国 IT 领袖峰会上,小马哥说了 “小程序已经成为中国编程者一个非常热门的编程环境和正在热情学习的语言环境” ,所以说小程序是一个趋势。 为什么说小程序是一个趋势呢,首先它基于微信,可以通过微信的关系链上流动获客;其次它无需安装,打开只需要用微信扫一扫即可,你再也不用担心你的 iphone 的存储空间不够了;第三它简单实用,不像现在的某宝,打开之后你还能认识它最开始是一个用来的支付的 APP 么,其实更多的用户安装 APP 只是使用它特定的一两个功能,其他的可要可不要,而小程序恰恰做到了这一点,简单独有的功能,这就够了。 前些日子,九大厂商「小米、华为、中兴、金立、联想、魅族、努比亚、oppo、vivo」联合推出了一个叫做「快应用」的项目,其实说白了,就是桌面快捷方式,无需安装,直接可以再桌面打开,类似于微信小程序,实现方式也类似与 React Native,Weex 之类的,性能介于 Web 和原生之间。看吧,小程序已经对一些厂商产生了威胁,要知道国内的手机厂商在手机上根本不赚钱,而赚钱的是广告和预装 app ,这就是为什么刚买的新手机上预装了那么多 app 的原因。而有了小程序,装一个微信就够了,谁还愿意花重金在手机上预装 app 啊,所以九大厂商也意识到了情势的严峻搞了个「快应用」出来。 今天正好看到「极客时间」上推出了关于小程序的课程,我毫不犹豫的跳进去了,来吧,长按下方图片识别二维码,成为一个小程序大神,走起… 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"推荐一些常看的公众号,不是互推","slug":"180324-推荐一些常看的公众号,不是互推","date":"2018-03-24T01:03:32.000Z","updated":"2018-03-24T09:44:21.496Z","comments":true,"path":"180324-推荐一些常看的公众号,不是互推.html","link":"","permalink":"https://dddreams.github.io/180324-推荐一些常看的公众号,不是互推.html","excerpt":"","text":"我倒是想互推呢,,, 看过很多推荐公众号的文章,总有人留言说是互推的吧,其实我倒是想互推呢,能让这些大佬推荐,能不火都不行,要知道他们的公众号读者至少 10+ ,能让他们推荐,那真是荣幸。 这几个号其实之前就推荐过,只所以再次推荐,是因为这一年来我一直跟随着这几个公众号成长的,他们的坚持给了我很大的帮助,无论是从认知,行业现状,技术发展等各个方面,18年的前三个月又懈怠了这件事,一直以来都想跟他们一样坚持做自己想做的事。「制定一个目标,想坚持一件事情。这一次,是认真的,真的是认真的」。 1、stormzhang stormzhang, 不是第一次推荐了,他是一个非常值得让人敬佩的人,非科班出身,自学编程,现在成为公司的技术管理者,坚持写作 5,6 年了,前段时间因为他的知识星球 「帅张和他的朋友们」 第二期续费,日入 10w+ ,目前付费用户 6000+ ,前几天还被知识星球官方采访 他不把运气放在眼里,用努力完成从学渣到职场高手的逆袭,因此他自己也做了总结,个人觉得这是一篇很不错的文章 一个新的里程碑。 2、MacTalk 池老师,老程序员,先后任职洪恩软件和用友集团,从事互联网和企业应用软件研发,还在锤子科技担任过总监,目前加盟极客邦科技,任总裁。喜爱编程和写作,坚持年轻时的理想。他的文章文风有趣,又有一点力量。去年极客帮团队上线的APP「极客时间」也是非常棒的产品,可以说是程序员的福利。 3、小道消息 冯老师,也是一个值得让人敬佩的人,常以不同与常人的思维思考问题,写文章也是,有独到的见解,时而风趣,时而讲真,在很多地方可以找到他的身影,推特、公众号、知识星球、微博等等,目前正在创业中,他的创业公司无码科技出品的产品「Readhub」是一个极简,有价值,注重与用户体验的产品。他们的产品特别重视用户体验,其中入住微信小程序的「Readhub」,「抽奖助手」也得到了用户的好评。 4、keso 怎么看 洪波,不是程序员,独立IT评论人,以犀利的笔锋、独到的观点著名。keso 的文字就像是重剑无锋一样,大巧不工,不花哨,不抖机灵,扎实,有力度,有深度。 关注上车,老司机要开车了。。。 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"推荐","slug":"推荐","permalink":"https://dddreams.github.io/tags/推荐/"}],"keywords":[]},{"title":"工作两年之后","slug":"171231-工作两年之后","date":"2018-01-03T01:03:32.000Z","updated":"2018-01-03T01:07:26.976Z","comments":true,"path":"171231-工作两年之后.html","link":"","permalink":"https://dddreams.github.io/171231-工作两年之后.html","excerpt":"","text":"我的 2017 年终总结 又是一年年尾,今年的年尾好像显得尤为重要似的,每个人都在谈论 “再见2017,你好2018” 的话题,朋友圈继圣诞帽的热潮之后,又掀起了 “我的 18 岁” 热潮,2017 年的我们越来越喜欢跟风刷存在感,相比前面两种热潮我更青睐于知乎上年终总结的热潮。 用两个字总结这一年,那就是「忙碌」,可是在写这篇总结的时候却发现可写的没有多少,说白了是收获没有多少,除了年龄上的收获外,其他的都没有那么明显了。 最大的变化这一年最大的变化就是每天上班做的第一件事,以往的时候上班第一件事先是打开掘金或者知乎刷几篇热门的技术文章,而今年上班第一件事必然是打开我们所开发的平台,点点自己负责的模块,看看系统是否运行正常,然后开始一天的工作。工作上,不在是整天埋头敲代码,更多的是考虑如何设计,如何分配,如何提高效率,一天的时间几乎被一些琐碎的杂事请所占据,从而显得疲惫了不少,也正是因为这些琐碎的杂事请,每天感觉什么也没做,反而使人更累,回头想想还是安安静静的敲代码来的爽快。技术上,几乎没有什么进展,每天忙碌下来,上班期间根本没有时间去看技术文档,下班后又感觉很累也就没心思学习了,再加上技术更新换代的速度太快,明显感觉到跟不上的节奏,想想也罢,一味的追逐新技术不是什么好事。生活上,那就更不用说了,一如既往的单调,除了熬夜越来越迟,头发越来越少之外在没有什么了。相比 16 年的总结「工作一年之后」,方向已经大有变化,但是技术人不能丢弃技术,这是毋庸置疑的。 对自己的投资不错,在这个变化莫测的时代,对自己的投资才是最有价值的投资,从16年开始,微信公众号成为了我获取信息的主要途径,我关注了大概有 200 多个公众号,有大公司的技术号,有个人的自媒体号,也有互联网大 V 的个人号,随着个人兴趣的变化,我也慢慢的开始关注产品类,运营类,投资理财类,心理学类等等的一些公众号,涉及领域之广泛,当然关注这么多公众号不可能每天都会去阅读,但是经常看的一些号,只要有更新就会去看。通过这些公众号,我也了解了一些付费平台的产品,订阅了罗元裳的「投资理财,怎么做才能获得最大收益」知乎 live,justjavac 的「前端工程师的入门与阶进」知乎live;订阅了朱赟在极客时间上的专栏「朱赟的技术管理课」,左耳朵耗子的「左耳听风」专栏;加入了 stormzhang 的知识星球「帅张和他的朋友们」。关注了这么多,也看了这么多,要说收获了什么我也说不上来,但是我相信长期下来收获是必然的。 「zhiheng博客」的运营这个公众号开通已经有两年多了,15,16 年几乎没写几篇文章,主要在今年虽然写的很少,但至少是坚持写了一年,并且获得了微信爸爸的原创保护,留言赞赏功能,虽然听说原创保护已经对所有公众号开放了[捂脸],粉丝长的也不多,但是还是要感谢一直关注我的读者们,你们的坚守与鼓励是我写下去的动力。17 年总的写了大概有 30 几篇文章,其中技术分享的文章不多,有几篇关于 GO 语言的,本来打算系统的学习这门语言,并以教程的形式发表,可是由于时间原因放弃了,之所以要学习 GO 语言,是因为 GO 比较年轻,在设计上都有前所未有的前瞻性,相信他将会成为未来的趋势。 结语忙碌的一年,回头看时,能让人历历在目的没有几件事,还是那句话,都不知道这一年干了个啥,已经结束了。明年呢,明年继续努力,少熬夜,多看书,多运动,多与异性交朋友。[斜眼笑] 2017 年 12 月 31 日冬 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"总结","slug":"总结","permalink":"https://dddreams.github.io/tags/总结/"}],"keywords":[]},{"title":"刷屏了的圣诞帽与技术无关","slug":"171223-刷屏了的圣诞帽与技术无关","date":"2017-12-23T06:46:17.000Z","updated":"2018-03-24T08:21:04.385Z","comments":true,"path":"171223-刷屏了的圣诞帽与技术无关.html","link":"","permalink":"https://dddreams.github.io/171223-刷屏了的圣诞帽与技术无关.html","excerpt":"","text":"借助这个圣诞节呼吁大家重视传统文化的传承理性过节 临近圣诞,大家都按捺不住自己激动的心情,最按捺不住的当属朋友圈了,这不,大清早的就被一条 @微信官方 的消息给刷屏了,原文是这样的 “请给我一个圣诞帽@微信官方” ,不过在朋友圈各种版本的都有,比如,“给老子来顶圣诞帽@微信官方”,“给我一个比特币@微信官方”,我只想说一句 “你咋不上天呢…”。 刷屏了的不仅仅是朋友圈,就连微信公众平台也被有关圣诞帽的文章给刷屏了,看过几篇之后大概知道了这是怎么回事。最初不知是从哪传来的谣言,说是在朋友圈发一条 “请给我一个圣诞帽@微信官方” 的动态,微信就会在你头像上加一顶圣诞帽,还有人分析说,微信是使用了人工智能,图像识别技术,自动识别出头像中的位置将帽子添加上去的,这更加增加了传奇性,引起了大家的好奇心,于是乎朋友圈就被这条消息刷屏了。其实,这一切只是一场骗局罢了,不信,发你的头像到后台,我帮你制作一个戴圣诞帽的头像。 既然知道是骗局,那么呼吁大家别在发朋友圈传播了,毕竟国家已经不提倡过洋节了,你不知道吗? 接下来才是重点关注的内容昨天看到一篇文章,国家终于出手,洋节,再见,之后我在网上查了文化部的相关文件,发文中的规定并没有这篇文章中说的那么夸张,但是我觉得,对于洋节这件事应该引起大众的关注,你有没有发现,大街上已经摆满了的圣诞树,各商铺门口已经摆满了穿着红色衣服的圣诞老人和贴满圣诞节商品打折降价的广告牌,各大电商网站也想通过次节赚一笔横财,降价打折的广告铺天盖地。反观我们的传统节日,今年国庆节除了诱人的 7 天假和大街上插着几面国旗外,貌似再也看不到有什么引人注目的活动了,中秋节除了卖月饼的那几家商店搞搞活动卖卖月饼外,好像也没什么值得关注的了,就连老人们最喜爱的春节,在年轻人眼里只不过是走亲访友的麻烦,在别说重阳节,清明节,端午节这些只有假期才能让人记得的节日了。 去年圣诞节的时候,有位在幼儿园教书的朋友,在朋友圈晒礼物,孩子们送的圣诞礼物,把她高兴坏了。我给她回复说,为什么不在教师节送,她一脸懵逼无言以对。幼儿园的孩子,都知道在圣诞节要给老师送礼物,难道这不是一件可怕的事吗,试想这批孩子们长大以后,在他们的观念里是不是会有 ”在中国圣诞节比春节更为重要“ 这样的想法呢???当然错不在孩子们,而在我们这一代人身上,对西方文化的接纳,再没有比我们这一代人更为敞开胸怀的了。 时代科技的发展,我们越来越与国际接轨,然而我们丢失的传统文化与传统道德远比你想象的要多,在我看来,传统节日的传承,远不止只有假期才能让人们根深蒂固。从孩子的角度来看,六一儿童节远比三天假期的端午节更有意义,其中的原因不仅仅是儿童节是孩子们的节日这么单一,所以在学校组织一些类似儿童节这样的活动还是有必要的。 最后,借助这个圣诞节呼吁大家重视传统文化的传承理性过节,尤其是广大教育工作者,对于洋节,最好能免则免,还有广大党员同志们,以后过洋节要小心喽,,,[斜眼笑] 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"12月发生的几件事","slug":"171218-12月发生的几件事","date":"2017-12-18T05:04:39.000Z","updated":"2017-12-18T05:34:19.277Z","comments":true,"path":"171218-12月发生的几件事.html","link":"","permalink":"https://dddreams.github.io/171218-12月发生的几件事.html","excerpt":"","text":"好汉不吃眼前亏。但是在另外很多时候,如果问题争执不下,也不要继续火上浇油,冷静下来,多收集一些数据材料与想更明白点再说。 又是忙碌的一周,上周通宵两夜赶出来的功能只用了一下午,还是有一点小失望的。今天认识了一个同是喜欢写作的朋友,聊起了写作,甚是投机,非科班出身,喜欢写作,一下子勾起了我的写作欲望,于是有了这篇 12 月发生的一些事。 世界互联网大会12 月 3 日世界互联网大会在乌镇举行,各大佬们的讲话和饭桌上的佳肴,相信在前些天已经风靡了大街小巷,我也不再过多的罗嗦。看了很多的评论和总结,有人说小马哥尽然谈起了创新,那不是扯淡吗,其实不然,近年来,随着国内网民的高速增长,国内的互联网企业也慢慢的进入了领头羊的角色,尤其是新一代企业家,对于走出国门他们有更大的胆识。意大利大街上的膜拜单车、印度人手中的小米手机、新加坡总理对扫码支付的惊叹等等,这都预示着我们也走上了创新的道路。早在几年前,李彦宏就说过,“当中国的网民数量成为世界第一后,我们就会先于美国碰到各种各样的互联网问题,如果能够先碰到这个问题,就有机会去先解决这个问题,创新就会源源不断的出现。这种优势,是中国的优势,是别的国家无法效仿的优势。”(出自公众号 keso怎么看 的中国互联网的自信是哪儿来的?)马化腾也曾说过,“山寨美国是全世界互联网的必经阶段,但是山寨这件事不会再持续下去,不建立自己的创新基因和文化,中国互联网的发展不可能长久。” 看看小马哥家的微信,小程序的创新也成了大家山寨的对象,支付宝做起了小程序,就连 Google 的 PWA 也有点小程序的理念了,不用安装,用完就走。有人说微信已经老了,其实微信最近更新挺频繁的,只是你不注意罢了,最近更新的功能有聊天记录的检索,可以通过日期检索;收藏功能的增加,现在收藏里可以添加图片,视频,富文本,位置,录音,还能写待办事项; 已撤回的消息再编辑,很多时候我们撤回消息不是发错人或发错群,而是写错字了,现在当你撤回消息,会有个“再编辑”的链接,点击那个链接,消息会出现在输入框,改改之后重新发送就行了,很是贴心; 微信一直在为用户需求和用户体验上下功夫,这才是真正为用户而打造的产品。反观某付宝,现在拿出来,还能认得出它刚开始是一个做支付的 APP 吗。 Google 开发者大会12 月 13 日 Google 开发者大会在上海举行,我在第一时间看了直播,感觉也没有那么激动,但是有好多自媒体作者激动的发文感慨,也许是他们去现场的缘故吧。这次大会振奋人心的是 Google AI 中国中心在北京成立,这为广大的学生及研究人员提供高质量 AI 及机器学习的教育支持。还有 Android 的蓬勃发展,Google 智能助理的推出,前面提到的 PWA 帮助中国开发者优化用户体验,PWA 也支持了国内大部分主流的浏览器,还有 Google 的分析系统等等。这似乎预示着 Google 在国内的回归,但是是否真的能回归,还需要更多是时间和更大的努力。 中兴 70 后程序员跳楼身亡12 月 14 日发在美篇上的一篇文章,70 后程序员在中兴通信大楼跳楼身亡。当事人欧某出身农村家庭,本科北京航空航天大学,在华为公司工作 8 年,期间考取南开大学的硕士研究生。近两年程序员群体被大众注意到,前有 WePhone 开发者被女友诈骗自杀,后有中兴 70 后程序员跳楼身亡,程序员的弱势在于工作性质,常年跟机器打交道,以为什么都跟机器一样讲道理,低估了人性的复杂与丑陋,加上工作的原因,习惯遇事自己独立解决 ,导致遇到事情不会妥善处理,更不会轻易请人帮助,一致酿成悲剧。在这我也提醒广大程序员同胞们,在工作之余,技术之外,也要多加强人际交往和沟通的能力及处事方式和处理人情世故,谁都知道,程序员大多是智商较高,但是情商较为缺乏,而人这一生情商远比智商更为有用一些。 最后,2017 年又接近尾声,年底往往事情比较多,在忙碌之余写作仍将继续,晚安,我的朋友们!2017 年 12 月 18 日凌晨 1 点。 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"会上瘾的今日头条","slug":"171203-会上瘾的今日头条","date":"2017-12-05T05:07:17.000Z","updated":"2017-12-05T05:14:40.294Z","comments":true,"path":"171203-会上瘾的今日头条.html","link":"","permalink":"https://dddreams.github.io/171203-会上瘾的今日头条.html","excerpt":"","text":"年轻时很容易想要“一鸣惊人”,急于证明自己,但其实,二十几岁恰恰是需要一个人定下心来,本本分分,一步步地去做事情,去积累的时候。年轻不怕失败,只要能学到东西,并且让自己一直处于成长的状态,你想要的都会不请自来。——蓑依 最近经常见好多同事,一有闲时间就在刷头条,我也好奇就安装了今日头条,大概看了一些有趣的段子什么的,就再没管,当我第二次打开头条的时候,发现铺天盖地的都是同一种类型的段子,把我看的不亦乐乎。后来又看了些我感兴趣的话题,当我再次打开头条看时,又是同一类型的话题,并且大多数是一些标题党,标题很吸引人,内容平平,没什么价值。时间一长我也跟同事们一样了,一有闲时间就不由自主的打开头条,而且看的不亦乐乎。当我发现自己大部分闲时间都被头条占据后,我毫不犹豫的卸载了。 记得大学时候,新闻客户端还是那个风格简洁,以红色为主色的网易新闻,每天上课前看看新闻,偶尔还会转发分享到微博(那时的朋友圈还没有现在这么流行)。当网易还沉浸在包揽用户的喜悦之中时,今日头条慢慢进入了人们的生活,以他独到的推荐算法,魔法般的将用户感兴趣的东西一股脑抛到你面前,你就像上瘾了似的每天笑的乐哈哈。再到后来,网易新闻也做起了推荐,丢弃了当初那个少女般的新闻类产品,和头条一样,无论内容好坏一股脑的抛给用户。不得不说头条的推荐算法很强大,侵蚀力也很强,甚至老牌的腾讯新闻,新浪新闻也被头条超越。他准确抓住了人性的弱点,那些热点文章都是在抓人的注意力,使用耸人听闻的标题,引诱你其点击,点击之后头条认为你对其感兴趣,然后给你推荐更多的垃圾信息。 据有关统计,头条的点击量已经超越了公众号的点击阅读量,单从每天发表数量来看,公众号原创输出远远大于头条的输出,但是在点击量上头条却占据了领先地。在我看来,这很正常,因为公众号文章用户有选择权,如果一个公众号输出的内容没有多大价值,用户可以取关;相反的头条无论内容质量如何都推荐给用户,几乎排在前面的文章都是利用机器推荐算法推荐给你的。但是公众号就不样了,我们知道公众号的文章阅读一部分是来自朋友的转发,那么你是相信朋友给你的推荐呢,还是相信机器算法给你的推荐? 其实头条上也有一些很不错的文章值得我们去阅读,只是那些吸引眼球的标题和远远不断的推荐,占据了你筛选信息的时间。在这个信息膨胀的互联网时代,如何筛选出真实,有价值,有深度的内容,还是需要一个有节操有底线的产品来为用户服务,从而节省人们更多的时间。 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"每天抽一点时间来阅读","slug":"171125-每天抽一点时间来阅读","date":"2017-11-27T05:30:50.000Z","updated":"2017-11-27T06:34:59.048Z","comments":true,"path":"171125-每天抽一点时间来阅读.html","link":"","permalink":"https://dddreams.github.io/171125-每天抽一点时间来阅读.html","excerpt":"","text":"真正的读书使瞌睡者醒来,给未定目标者选择适当的目标。正当的书籍指示人以正道,使其避免误入歧途。 刚在上篇文章中说完坚持这件事,就发现一款很不错的打卡产品,叫做 “小打卡” 的微信小程序,在这个平台上你可以建立自己的圈子,邀请几位好友,分享一些内容;我随便刷了刷,上面的圈子主题还挺多,有运动的,有写作的,有阅读的,有写一些读后感的,还有学英语的,每天读一段英文句子,我觉得这种模式就很好。一些兴趣爱好相同的人聚在一起,互相学习,互相讨论,看着每天打卡学习的小伙伴都在努力提升自己,你还能安稳的享受这生活的安逸么?我也在小打卡上开了一个圈子,叫做 “每日心情” ,我的主题内容很简单,每天发表一点鸡汤,心情,配上一张唯美的图片,目的在于锻炼每天坚持做这件事,如果你能坚持打卡一年半载,或许这已经成为你的习惯,那么你可以尝试做一些更有意义的事,比如写作,阅读。 前面写到过的写作,有人也不止一次的说起过,几乎都是同样的问题,就是我也想写,但是每次写的时候总是不知道要写啥,或者害怕写的东西被别人看到等等,于是引发了今天这个话题–阅读。 我所说的阅读,不是传统的书籍阅读,而是指碎片话阅读,或许有人会说,碎片话阅读那只是噱头,其实不然,现在的社会潮流,手机是阅读的很好工具,智能手机几乎覆盖了所有人,尤其是年轻人,吃饭,坐车,睡觉都几乎已经手机不离手了,只要抽出刷朋友圈,刷微博三分之一的时间用来阅读,那每天的阅读量估计能达到 5w 字以上了。当然我也不反对书籍的阅读,如果要系统的学习一门技术,我还是建议去买一本不错的书,因为书上的知识比较系统,讲的比较全面,适合那些从入门到深入学习的人;反过来,你想通过碎片化阅读去学习一门技术,那是不可能的,就算学到一点知识,那也可能只是一个知识点,仅此而已。 这是一个信息化时代,各种各样的信息满天飞,我们怎么能在这些信息中筛选出自己感兴趣有价值的信息呢,我推荐使用微信公众号,选择关注一些技术牛人大佬的公众号,通常会不定期的写技术分享,或者业界一些有趣的事,这要比在头条,微博上筛选信息来的快。其实还有很多获取阅读的途径,比如知乎专栏,得到专栏,极客时间专栏,知识星球等等。这些产品的模式是近几年业界的一个亮点 “知识付费”,他们输出的知识内容都要求付费。我是不反对这种模式的,首先对于作者来说,付费是更大鼓励与认可,输出的文章自然也会保证质量;对读者来说,我付费了,就会花更多是时间去思考作者的观点与想法;所以知识付费是双赢互利的。 我每天都会花大量的时间去阅读,除了公众号以外,我也订阅了几个专栏,包括朱赟在极客时间上的“管理课程”,左耳朵耗子在极客时间上的“左耳听风”,stormzhang 在知识星球上的 “帅张和他的朋友们” ,我在这些平台上获得了很多有价值的东西,包括认知,管理,技术,理财等等,所以不管你处于什么阶段,每天抽出一点时间来阅读,对你的见识和眼界都是有很大帮助的。 更多文章请关注微信公众号: zhiheng博客 转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"如何坚持不断的做一件事","slug":"171121-如何坚持不断的做一件事","date":"2017-11-21T13:06:24.000Z","updated":"2017-11-22T11:01:59.613Z","comments":true,"path":"171121-如何坚持不断的做一件事.html","link":"","permalink":"https://dddreams.github.io/171121-如何坚持不断的做一件事.html","excerpt":"","text":"书不记,熟读可记;义不精,细思可精;惟有志不立,直是无着力处。 上篇文章说了写作之后,有很读者就在问我 “道理我都懂,但是就是坚持不下来”,的确是这样。我见过很多这样的例子,刚开始的时候心血来潮,噼噼啪啪写了很多,后来慢慢的就不见发表文章了,其实我也是一样,有时候会写很多,有时候又感觉没什么可写,最终的原因还是坚持不下来;那么如何坚持不断的做一件事呢? 最近看公众号文章发现一个有趣的现象,公众号阵营好像一波一波的,比如,前面提过的 stormzhang 阵营的有 python 之禅,一个程序员的日常,高效率工具搜罗,DeveloperPython 等,他们的公众号几乎都互相推荐过,运营模式基本相似,有做 Android 的,有做 python 的,有收集一些好用的工具的;其实有一部分人是受到 stormzhang 的影响才运营起公众号的,也包括我,我在他那也受了很多启发,不论是写作,阅读,理财,职业规划,stormzhang 说的都很有自己的观点。另一个阵营的是以池建强老师为首的,包括前面提到过的 嘀嗒嘀嗒,caoz的梦呓,二爷鉴书,keso怎么看 等等,这些公众号也是通过 嘀嗒嘀嗒 推荐后我才关注的,读 嘀嗒嘀嗒 的简介时才了解到,她是被池建强老师拉下水的,然后就喜欢上了写作,并持续不断的在公众号的更新。也许这就是他们坚持写作的原因之一,互相推荐、互相鼓励、同时也互相学习。 其实这种方式坚持做一件事是非常有效的,还记得大学时候,做什么事都要拉个伙伴一起,记得一个哥们为了追妹子,每天都拉我去跑步,跑了一段时间妹子没追成,倒是养成了跑步的习惯,每天都去跑两圈,后来毕业之后各奔东西这个习惯没坚持多久就丢掉了。所以想坚持做一件事,找个小伙伴一块坚持是一个行之有效的方法。 坚持做一件事,让其形成习惯,说起来简单做起来却难于上青天,不如从一件小事做起,比如早睡早起,算了,早睡早起只适合中老年人的习惯,对于年轻人很少有人能坚持做到这件事。你可以从一件比较感兴趣的小事做起,比如,有人喜欢鸡汤,可以每天读一小段鸡汤,或者直接复制到朋友圈;比如,可以关注一个叫每日打卡的公号,每天在上面签到打卡,养成这个习惯从而锻炼你坚持做事的态度等等。 更多文章请关注微信公众号: zhiheng博客 转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"未来必不可少的一项技能","slug":"171119-未来必不可少的一项技能","date":"2017-11-20T01:58:18.000Z","updated":"2017-11-20T02:55:12.706Z","comments":true,"path":"171119-未来必不可少的一项技能.html","link":"","permalink":"https://dddreams.github.io/171119-未来必不可少的一项技能.html","excerpt":"","text":"未来必不可少的一项技能–写作 公众号已经有一个多月没有更新了,最近一直在思考,如何更好的去运营这个公号,以前所写的文章都比较杂乱,涉及到了很多方面,回头看时,有价值的文章很少,所以最近就一直在想,如何提升文章内容的优质性,引发读者一些思考,真正的能帮助读者进行自我提升。 本来还想重新申请一个公号,后来想想还是算了,一是不想扔掉之前所写的文章;二是同时运营两个公号费时费力,还不如把时间花在如何更好的输出有价值的东西。 在这期间我关注了很多文章写的不错的公号,主体大都是一些业界大佬,有做管理的,有做产品的,也有做技术的,不管是什么方向,他们总是能输出一些令人惊叹的文章,并能持续不断的输出。不可否认他们在业界摸爬滚打这么多年积累的经验很丰富,但是要将这些自身的经验转换成文字,转换为对别人有帮助的东西,还是要靠一项技能,那就是今天所说的–写作。 说几个公众号故事吧 1、stormzhang 如果你经常刷知乎,公众号,或者是知识星球,那肯定会见到他的身影。我关注他的公号有一年多了,之前也有推荐过的他的公号,他写的文章总是能打动人,无论内容是否对你有价值,但是看他的文章感觉上就是一种享受,当然让你享受的文章内容肯定不会差。他的公号主要分享一些个人成长,理财,职业发展相关的,同时他还运营着一个叫做“帅张和他的朋友们”的知识星球,里面的气氛很好,大家互相帮助,互相讨论一些问题,也可以直接向他提出一些问题,他的回答也是有依有据。他是一个让知识变现的例子,而这也要归功于一项必不可少的技能–写作。 2、滴答滴答 这个公号的主体可就牛了,她叫朱赟,毕业于中国科技大学少年班,获得美国萊斯大学攻读硕士和博士,Square 公司的第一位华人女工程师,现任硅谷 Airbnb 公司的支付团队的技术经理。我也是前几天看池建强老师写的一篇文章中得知这位牛人的,然后就关注了,翻了翻她之前写的文章,发现太能写了,大多都是自己的一些经验总结,但是涉及了技术、管理等多方面,而且都有自己的独特的观点。她的公号粉丝已经有 1w+ 了,但是看不到有广告,人家任性的说了 “我不差钱”。最近她在极客时间的 APP 上开了一个专栏 “朱赟的技术管理课”,为了跟紧大牛的步伐我花了 58 元大洋订阅了。所以真正能实现财富自由不差钱的技能–写作。 说着两个故事呢,证明一个问题,无论你是做技术,产品,还是运维,或者说无论你是否在互联网行业,都应该对自己定期的做总结,做归纳。昨天看 “caoz的梦呓” 的一个举例,一个没文化,没多过书的家长,把孩子培养名校高材生,问他怎么培养的,说其实很简单,孩子明天回来,让他给自己讲讲学校里都讲了啥,自己学会了啥,让孩子给自己当老师,对知识的掌握,对孩子的表达能力和总结能力的提高,是非常大的。而做总结归纳的一个非常有效的途径就是–写作。 更多文章请关注微信公众号: zhiheng博客 转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"还有什么理由不奋斗","slug":"170923-还有什么理由不奋斗","date":"2017-09-23T07:16:39.000Z","updated":"2017-09-23T08:38:03.361Z","comments":true,"path":"170923-还有什么理由不奋斗.html","link":"","permalink":"https://dddreams.github.io/170923-还有什么理由不奋斗.html","excerpt":"","text":"好的程序员即使在过单行道时也总是会环顾两边。 iPhone X 都可以人脸识别了,电子身份证即将面世了,Goole 又要放大招了(HTC 被收购),闲得无聊可以跟手机聊会天了,或许有一天人们再也不用养宠物了,机器人美女可以解决你的空虚寂寞了,,,那么你,还有什么理由不去努力奋斗了。 1前几天看到一篇文章,叫做你的见识,决定了你能走多远,真是这样,自己深有体会。还记得第一次在北京坐地铁,总是害怕坐过站,人多的时候害怕挤不下去。后来生活了一段时间,就连早上7,8点西二旗的13号线都能挤上去,还有什么做不了的。离开北京,来到二线城市不算是决策的失误,只要还有一颗年轻向上的心,也可以继续做自己喜欢的事,敲自己喜欢的代码。很多人会抱怨北上广房价高,空气不好,但还是继续待着不离开,大部分原因是由于他们有一颗年轻不服输的心。在那里是人才的聚集地,也是知识的发源地,很多创新的想法都起源于这些地方。 2我关注的一个公众号作者,我们都叫他“张哥”(不是他年龄大),他生活在上海,是一个 Android 程序员,活跃于各大社区,如果你经常逛知乎,玩公众号或者知识星球,应该能找到他的身影,常常分享一些赚钱的方法,很多还是挺靠谱的,我自己也试过几次,还是可以的。为什么说这个人呢,前几天他分享了一篇叫做 一位读者关于买房的困惑 的文章,对我启发很大,前段时间一直处于低迷状态,其中就有房子的原因,甚至浮现了回老家的念头。他说的很对,在大城市生活的人,其见识、视野绝对是我们无法比拟的。虽然我们在所在的城市买不起房,但是我们还年轻,就算是人生的四分之一还过一点已经没了,我们依然有不服输的心,有好的想法,有惊人的创意;就算没有创意没有想法,我们还有附带中华民族的优良传统,为自己的下一代着想的无私奉献。如果我现在回去了,那么我的下一代,还会背着行囊走同样的路,他们还是会面临着高房价的威逼,而站在岔路口徘徊,所以,,,还有什么理由不奋斗呢。 3PS:还有什么理由不奋斗… 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"go教程-GO常用命令","slug":"170918-go教程-GO常用命令","date":"2017-09-18T07:42:07.000Z","updated":"2018-08-14T06:57:56.270Z","comments":true,"path":"170918-go教程-GO常用命令.html","link":"","permalink":"https://dddreams.github.io/170918-go教程-GO常用命令.html","excerpt":"","text":"编程是 10% 的科学,20% 天份,和 70% 的让这天份符合科学. 上一节中我们已经用到了一些 go 的命令和工具,今天来系统的看看这些命令有什么神奇黑科技。 go 常用命令Go 语言自带有一套完整的命令操作工具,你可以通过在命令行中执行 go 来查看它们: 这些命令在我们平常开发中经常会用到,我们来看看一些常用的命令: go buildgo build 命令用于编译我们指定的源码文件或代码包以及它们的依赖包。 如果我们在执行 go build 命令时后面不跟任何代码包,那么命令将试图编译当前目录所对应的代码包,例如,我们在上一节go教程-GOPATH配置与本地教程安装中编译 mymath 包那样,当你执行 go build 时,他不会产生任何文件,因为该包中只包含库源码文件,或者测试源码文件。如果你需要在 $GOPATH/pkg 生成相应的文件,那就的执行 go install 了。 如果是 main 包,当你执行 go build 之后,会在当前目录下生成一个可执行文件,因为 mian 包是命令源码文件,是程序的入口文件,所以会生成可执行文件。如果你需要在 $GOPATH/bin 下面生成相应的文件,你需要执行 go install 或者使用 go build -o 路径/a.exe . 知识点:GO 语言的源码文件有三大类:命令源码文件、库源码文件和测试源码文件。他们的写法各有特点。命令源码文件是可执行的程序入口。哭源码文件锁一般用于集中放置待被使用的程序实体(全局常量、全局变量、接口、结构体、函数等等)。测试源码文件只要用于对前两种源码文件中的程序功能进行测试。 当然我们可以直接在 go build 后面跟上包路径,想这样 go build github.com/shure/mymath 也是可以执行的,这是因为我们已经将项目根目录添加到环境变量 GOPANTH 中了,这样我们就可以在任意目录执行这个命令了。 go build 还有很多参数,例如: -a 强行对所有涉及到的代码包(包含标准库中的代码包)进行重新构建,即使它们已经是最新的了。-n 打印编译期间所用到的其它命令,但是并不真正执行它们。 更多的参数就不细说了,有兴趣的朋友可以在此查阅https://studygolang.com/articles/5226 go clean这个命令是用来移除当前源码包里面编译生成的文件。这些文件包括: 使用 go build 命令时在当前代码包下生成的与包名同名或者与Go源码文件同名的可执行文件。 执行 go test 命令并加入-c标记时在当前代码包下生成的以包名加“.test”后缀为名的文件。 _obj/ #(旧的 object 目录) _test/ #(旧的 test 目录) _testmain.go #(旧的 gotest 文件) test.out #(旧的 test 记录) build.out #(旧的 test 记录) .exe #(go build 产生) .test.exe #(go test 产生) 等等 一般的在 github 上提交代码时,运行此命令清除后提交源码文件。 go docgo doc 命令可以打印附于Go语言程序实体上的文档。我们可以通过把程序实体的标识符作为该命令的参数来达到查看其文档的目的。 godoc 是一个很强大的工具,同样用于展示指定代码包的文档。在Go语言的1.5版本以后,它是一个内置的标准命令。 godoc -http=:6060 标记 -http 的值 :6060 表示启动的Web服务器使用本机的6060端口。之后,我们就可以通过在网络浏览器的地址栏中输入 http://localhost:6060 来查看以网页方式展现的Go文档了。 go fmt一般的在其他编程语言中,对于代码风格没有严格的要求,但是 Go 语言则有标准的风格,对代码格式进行了强制,比如:左大括号必须放在行尾。不按照此格式的代码将不能编译通过,为了减少排版的问题,go 工具提供了一个 go fmt 命令,它可以帮助格式化你的代码文件,你只需要执行 go fmt xxx.go 你的代码将会被修改为标准格式。其实很多开发工具已经集成了该工具,当你按下 Ctrl + S 时,IDE 自动调用了该命令,进行了代码格式化。 更多时候使用 gofmt 而且需要带参数 -w ,否则格式化结果不回写入文件。gofmt -w src 可以格式化整个项目。 go getgo get 可以根据要求和实际情况从互联网上下载或更新指定的代码败一级依赖包,并对他们进行编译和安装。目前支持的有 BitBucket、Github、Google Code和Launchpad。这个命令实际上操作了两步:第一步是下载源码包,第二步是执行 go install 。 为了 go get 能正常工作,你必须安装相应的源码管理工具,比如:Github 使用 Git,Google Code 使用 hg 等。 go install命令 go install 用于编译并安装指定的代码包及它们的依赖包。当指定的代码包的依赖包还没有被编译和安装时,该命令先去处理依赖包。与 go build 命令一样,传给 go install 命令的代码包参数,应该可以导入路径的形式提供。并且, go build 命令的绝大多数标记也都可以用于 go install 命令。 实际上 go install 分成了两步操作:第一步生成结果文件(可执行文件或.a包),第二步会把编译好的结果移动到 $GOPANTH/pkg 或者 $GOPATH/bin. go rungo run 命令可以便宜源码并运行命令源码文件。由于包含了编译动作,因此可接受所有用于 go build 命令的标记,它不允许有多个包含 main 函数的命令源码包作为参数。 go test执行这个命令,会自动读取源码目录下面名为 *_test.go的文件,生成并运行测试用的可执行文件。默认的情况下,不需要任何的参数,它会自动把你源码包下面所有test文件测试完毕. 其他命令go 还提供了其它很多的工具,例如下面的这些工具 go fix 用来修复以前老版本的代码到新版本,例如 go1 之前老版本的代码转化到 go1 go version 查看 go 当前的版本 go env 查看当前 go 的环境变量 go list 列出当前全部安装的 package 参考与相关链接参阅书籍:《Go Web 编程》 go 中文社区:https://studygolang.com/articles/5226 go 命令教程:https://www.jianguoyun.com/p/DW-1aogQ2vP0BRjTkiU (访问密码: kmNYam) 本教程 Girhub 地址:https://github.com/dddreams/go-tutorial 更多文章请关注微信公众号: zhiheng博客 如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"go","slug":"go","permalink":"https://dddreams.github.io/tags/go/"}],"keywords":[]},{"title":"go教程-GOPATH配置与本地教程安装","slug":"170907-go教程-GOPATH配置与本地教程安装","date":"2017-09-07T11:43:49.000Z","updated":"2018-08-14T06:57:52.107Z","comments":true,"path":"170907-go教程-GOPATH配置与本地教程安装.html","link":"","permalink":"https://dddreams.github.io/170907-go教程-GOPATH配置与本地教程安装.html","excerpt":"","text":"贵有恒,何必三更起五更眠。最无益,只怕一日曝十日寒。 今天来学习 GOPATH 环境变量配置与工作空间,下一节将会学习到 GO 的一些常用命令。 GOPATH 配置go 命令依赖一个重要的环境变量:$GOPATH 在类似 Unix 环境大概这样设置: sh export GOPATH=/home/apple/mygoWindows 设置如下,新建一个环境变量名称叫做 GOPATH: GOPATH=c:\\mygo GOPATH允许多个目录,当有多个目录时,请注意分隔符,Windows是分号,Linux系统是冒号。当有多个 GOPATH 时,默认会将 go get 的内容放在第一个目录下 $GOPATH 目录约定有三个子目录: src 存放源代码(比如:.go .c .h .s等) pkg 编译后生成的文件(比如:.a) bin 编译后生成的可执行文件(为了方便,可以把此目录加入到 $PATH 变量中) GOPATH 是一些列用于go来查找包的目录列表. 使用 import “包名” 的时候, 如果在 GOROOT 里找不到, 应该会转向到你的 GOPATH 里去寻找. 注:GOPATH 和 GOROOT 不要设置在同一目录下,是因为不想你新安装的包, 污染了核心 go 的 pkg 和 src 文件。 应用目录一般的,新建应用或者代码包时,都是在 src 目录下新建一个文件夹,文件夹的名称就是包名称,也可以有多级目录,例如:在 src 目录下新建了 $GOPATH/src/github.com/shure/mymath,那么这个包的路径就是github.com/shure/mymath,包名称是最后一级目录 mymath 然后在 github.com/shure/mymath 下新建文件 sqrt.go 内容如下123456789package mymathfunc Sqrt(x float64) float64 { z := 0.0 for i := 0; i < 1000; i++ { z -= (z*z - x) / (2 * x) } return z} 这样我们就创建了一个 mymath 的包,建议 package 的名称和目录名保持一致。 编译应用编译应用 GO 提供了两种方式 1、进入对应的应用包目录,执行 go install 就可以安装了。 2、在任意目录执行 go install mymath 安装之后进入 pkg 目录下就可以看到 mymath.a 的文件,这个 .a 文件就是应用包。接下来我们来调用它: 新建应用 mathapp/main.go ,内容如下:12345678910package mainimport ( \"fmt\" \"github.com/shure/mymath\")func main() { fmt.Printf(\"Sqrt(2) = %v\\n\", mymath.Sqrt(2))} 然后进入该目录执行 go build 命令,就会在该目录下面生成一个 mathapp 可执行的文件,Linux 下运行 sh ./mathapp, Windows 下运行 ./mathapp.exe 就会输出 Sqrt(2) = 1.414213562373095 。 继续执行 go install 命令安装,这时就会在 $GOPATH/bin/ 目录下生成一个 mathapp 的可执行文件,同样可以运行它,以 Windows 为例:./mathapp.exe 并打印 Sqrt(2) = 1.414213562373095 获取远程包GO 提供了一个获取远程包的工具 go get ,目前go get支持多数开源社区(例如:github、googlecode、bitbucket、Launchpad) 注意:使用这个命令必须安装对应开源平台的源码控制工具,例如:github 使用 git ,googlecode 使用 hg。 我们可以使用这个工具来获取 GO 官方提供的教程,安装至本地方便查阅,这对于国内用户来说是非常必要的。中文版教程 github 地址 https://github.com/Go-zh/tour 直接在你 $GOPATH 的目录下运行 $ go get -u github.com/Go-zh/tour/gotour 等安装完成之后,会在 $GOPATH/bin 目录下生成一个 gotour 的可执行文件,运行该文件就会在你默认浏览器中打开教程。若你想安装该教程的英文版,直接执行 go tool tour 命令即可安装。 go get 本质上可以理解为首先第一步是通过源码工具 clone 代码到 src 下面,然后执行 go install 在代码中如何使用远程包,很简单的就是和使用本地包一样,只要在开头 import 相应的路径就可以 import "github.com/shure/mymath". 应用目录结构上面例子的目录结构 123456789101112131415161718$GOPATH bin |--mathapp.exe pkg |--windows_amd64 // 相应平台 |--github.com |--shure |--mymath.a src |--github.com |--Go-zh |--shure |--mathapp |--main.go |--mymath |--sqrt.go |--hello |--hello.go 参考与相关链接参阅书籍:《Go Web 编程》 go 官方教程:https://tour.golang.org go 中文版教程本地安装:https://github.com/Go-zh/tour go 中文版教程:http://tour.go-zh.org 本教程 github 地址:https://github.com/dddreams/go-tutorial 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,转发、分享、赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"go","slug":"go","permalink":"https://dddreams.github.io/tags/go/"}],"keywords":[]},{"title":"go教程-go环境搭建","slug":"170904-go教程-go环境搭建","date":"2017-09-05T03:38:09.000Z","updated":"2018-08-14T06:57:43.962Z","comments":true,"path":"170904-go教程-go环境搭建.html","link":"","permalink":"https://dddreams.github.io/170904-go教程-go环境搭建.html","excerpt":"","text":"Go is an open source programming language that makes it easy to build simple, reliable, and efficient software. 这一节我们开始环境搭建 安装可在官网下载安装 golang (https://golang.org/) 由于 Google 在国内的限制,需要科学上网方可访问官网,也可以访问中文版的文档 golang (http://docscn.studygolang.com/),Linux,Mac 用户按照文档上的提示安装即可。Windows 用户更简单,下载安装包一路 next 便可。 配置环境变量Linux,Mac 用户 若安装在 /usr/local/go 目录,要将 /usr/local/go/bin 添加到 PATH 环境变量, 你需要将此行添加到你的 /etc/profile(全系统安装)或 $HOME/.profile 文件中: 1export PATH=$PATH:/usr/local/go/bin 若安装指定目录,你需要设置 GOROOT 环境变量来指定安装位置。12export GOROOT=$HOME/goexport PATH=$PATH:$GOROOT/bin 注:要使环境变量生效,你需要重启所有打开的终端。 Windows 用户: 一般情况 Windows 安装后会自动添加至 path 中,无需配置,可以使用 go env 命令查看,该命令会提示 GOROOT, GOPATH 等变量,若能看到这些变量,说明已将 go 目录添加到 path 环境中。若报错,需要新建环境变量 GOROOT 值为 go 的安装路径,并将 GOROOT 添加至 path 中,$GOROOT/bin 安装测试新建一个 hello.go 的文件,文件内容为:1234567package mainimport \"fmt\"func main() { fmt.Printf(\"hello, world\")} 用 go 工具运行它:12$ go run hello.gohello, world 若看到 hello world 字样,说明已经安装成功。 卸载或更新 Go要从你的系统中移除既有的Go安装,需删除 go 目录。 在 Linux、Mac OS X、和 FreeBSD 系统下通常为 /usr/local/go, 在 Windows 下则为 c:\\Go。 你也应当从你的 PATH 环境变量中移除 Go 的 bin 目录。 在 Linux 和 FreeBSD 下你应当编辑 /etc/profile 或 $HOME/.profile。 若你是通过Mac OS X 包安装的 Go,那么你应当移除 /etc/paths.d/go 文件。Windows 下删除环境变量即可。 相关链接golang 官网 https://golang.org/ 中文版文档 http://docscn.studygolang.com/ 本教程 Github 地址 https://github.com/dddreams/go-tutorial 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,转发、分享、赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"go","slug":"go","permalink":"https://dddreams.github.io/tags/go/"}],"keywords":[]},{"title":"go教程-GO语言简介","slug":"170905-go教程-GO语言简介","date":"2017-09-02T07:38:16.000Z","updated":"2018-08-14T06:57:48.336Z","comments":true,"path":"170905-go教程-GO语言简介.html","link":"","permalink":"https://dddreams.github.io/170905-go教程-GO语言简介.html","excerpt":"","text":"与其站在选择的路口踟蹰,不如大胆地Just Go! 很早之前就想学习 go 语言了,今天终于开了个头,以教程的形式记录在此。纵观编程教程史,大多数的教程开篇便是环境搭建和最为经典的入门程序 Hello World ,我也不例外,按照惯例来,先从简介与环境搭建开始。 起源Go 语言起源 2007 年,并于 2009 年正式对外发布。在 2010 年 1 月 8 日被 Tiobe(闻名于它的编程语言流行程度排名)宣布为 “2009 年年度语言”。 在 Go 语言出现之前,开发者们总是面临非常艰难的抉择,究竟是使用执行速度快但是编译速度并不理想的语言(如:C++),还是使用编译速度较快但执行效率不佳的语言(如:.NET、Java),或者说开发难度较低但执行速度一般的动态语言呢?显然,Go 语言在这 3 个条件之间做到了最佳的平衡:快速编译,高效执行,易于开发。 特性 构建速度动态语言将快速编译作为自身的一大亮点,像 C++ 那样的静态语言一般都有非常漫长的编译和链接工作。而同样作为静态语言的 Go 语言,通过自身优良的构建机制,成功地去除了这个弊端,使得程序的构建过程变得微不足道,拥有了像脚本语言和动态语言那样的高效开发的能力。Go 语言中另一个非常重要的特性就是它的构建速度(编译和链接到机器代码的速度),一般情况下构建一个程序的时间只需要数百毫秒到几秒。C 语言中“头文件”的概念却导致越来越多因为依赖关系而使得构建一个大型的项目需要长达几个小时的时间,而Go 语言采用包模型这个模型通过严格的依赖关系检查机制来加快程序构建的速度,提供了非常好的可量测性。 垃圾回收由于内存问题(通常称为内存泄漏)长期以来一直伴随着 C++ 的开发者们,Go 语言的设计者们认为内存管理不应该是开发人员所需要考虑的问题。因此尽管 Go 语言像其它静态语言一样执行本地代码,但它依旧运行在某种意义上的虚拟机,以此来实现高效快速的垃圾回收. 协程与通道Go 语言的另一个目标是对于网络通信、并发和并行编程的极佳支持,从而更好地利用大量的分布式和多核的计算机,这一点对于谷歌内部的使用来说就非常重要了。设计者通过 goroutine 这种轻量级线程的概念来实现这个目标,然后通过 channel 来实现各个 goroutine 之间的通信。他们实现了分段栈增长和 goroutine 在线程基础上多路复用技术的自动化。 参考链接: https://segmentfault.com/a/1190000005666535 平台支持Go语言设计支持主流的32位和64位的x86平台,同时支持在 Windows, 苹果Mac OS X, Linux 和 FreeBSD 操作系统。 开发工具LiteIDE 是一款专门为Go语言开发的跨平台轻量级集成开发环境(IDE),由QT编写。下载 https://github.com/visualfc/liteide 其他主流的开发工具几乎都支持 GO 语言的开发,包括 Sublime text , Eclipse, IntelliJ Gogland 和 VIM 等。 相关链接Gogland 官网 https://golang.org/doc/ 知乎 https://www.zhihu.com/topic/19625982/hot 推荐阅读 《Go语言圣经》百度网盘 提取密码: 6vkg 本教程 Github 地址 https://github.com/dddreams/go-tutorial 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,转发、分享、赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"go","slug":"go","permalink":"https://dddreams.github.io/tags/go/"}],"keywords":[]},{"title":"公众号接入聊天机器人的通知","slug":"170829-公众号接入聊天机器人的通知","date":"2017-08-29T11:59:57.000Z","updated":"2017-08-29T12:58:25.863Z","comments":true,"path":"170829-公众号接入聊天机器人的通知.html","link":"","permalink":"https://dddreams.github.io/170829-公众号接入聊天机器人的通知.html","excerpt":"","text":"世界尽头的地方,是雄狮落泪的地方,是月亮升起的地方,是美梦诞生的地方。 一直以来,我都希望有人在公众号后台留言,但是留言的人没有几个,更多的原因是由于我公众号用户群体的差异化所导致,对于很多话题不感兴趣,不过我还是会一如既往的写我自己感兴趣的东西,折腾自己感兴趣的技术或者工具。 图灵机器人的接入前段时间我在一篇文章 学习英语的重要性 的文章中提到要做一个聊天的小程序,但是对于小程序自己没有实际的做过,学习起来需花费一定的时间,所以我在公众号的后台接入了图灵机器人聊天程序,感兴趣的可以在后台发消息体验。可以提一些问题,查询日常的公交,天气,翻译等等。 哈哈,是不是很神奇呢!快来试试吧! zhiheng聊天上面也提到了,由于微信小程序花费的时间比较多,于是我做了一个 web 端的 zhiheng 聊天程序,地址在 zhiheng 聊天, 贴一张聊天的效果图 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,转发、分享、赞赏才是真爱 [斜眼笑]","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"记一次亲子旅行","slug":"170818-记一次亲子旅行","date":"2017-08-18T02:21:15.000Z","updated":"2017-08-18T06:09:37.281Z","comments":true,"path":"170818-记一次亲子旅行.html","link":"","permalink":"https://dddreams.github.io/170818-记一次亲子旅行.html","excerpt":"","text":"大自然中的各种现象,包括小鸟、小溪、水仙和绵羊,都是不可或缺的,因为它们能矫正和治疗城市人倍感困顿的心灵。 版权声明:本文为朋友圈转载文章,未经博主允许不得转载。 关于带孩子旅游,说法颇多,尤其是婴幼儿的旅游。很多人认为孩子太小,没有出行的意义和必要,所谓玩也不会,看了也记不住,倒是舟车劳顿,四处颠簸,影响孩子的吃食,睡眠,加上小孩子适应能力有限,感冒拉肚的实在让孩子遭罪。 去年暑期,我们是在兜兜1岁半的时候去的北京。在纠结了很久飞机到底对婴儿耳朵有没有影响,还是大包小包的去了,奶粉奶瓶尿裤纸巾,枕头衣服药品辅食,我们俩只带了一套衣服就已经累趴了,刚会走路不久的跌跌撞撞大部分还得靠人肉背抱,不知道他看到了些什么,只记得对地铁里的拉手炒鸡感兴趣,哪管他人多拥挤,自顾自的攀上爬下,一言不合就让哭喊声响彻所及之处,八月份的北京,实在热的难受,室内外温差太大,他很快就感冒了,北京儿科的拥挤可以想象,所以去年的出行就只留下天安门前的一张照片而已,一路心酸使我和老公不约而同的发誓,在六岁之前绝不再带他出门! 可是没多久我们就食言了,我一方面是打着孩子的旗号满足自己的玩心,另一方面我还是认为多些见闻和刺激是有益的,不在于他记住了些什么,或者学会了描述什么,只要能遵从他的好奇心,体验到过程的愉悦甚至是不悦就是好的,我认可“修剪理论”的合理性。更重要的是旅行不是一种虚荣和攀比,更不能功利,两个人的旅行会很惬意,三个人的旅行很深邃,爸爸的深度参与会是高质量的陪伴。 今年2岁半的出行顺利了很多,简易手推车解放了我们的双手,随时随地倒头就睡,完全不需要绘本儿歌故事之类的哄睡伎俩,睡醒了满血复活继续游山玩水。带孩子离开日常生活的场所,让他认识家人以外的人感觉也很奇妙,在火车站,我们等车等的极不耐烦的时候,他跟一对大学生情侣玩的笑声震天,孩子带给我们的体验是多维的。 虽然不赞成乐嘉的沙漠徒步教育,但我认为力所能及的情况下,带孩子旅行是可以提倡的,辛苦是必然的,收获也是满满的。 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"旅行","slug":"旅行","permalink":"https://dddreams.github.io/tags/旅行/"}],"keywords":[]},{"title":"Google 又多了两位大牛","slug":"170816-Google-又多了两位大牛","date":"2017-08-16T03:34:21.000Z","updated":"2017-08-25T05:30:28.262Z","comments":true,"path":"170816-Google-又多了两位大牛.html","link":"","permalink":"https://dddreams.github.io/170816-Google-又多了两位大牛.html","excerpt":"","text":"编程是 10% 的科学,20% 天份,和 70% 的让这天份符合科学。 昨天在刷公众号时,看到一篇文章Swift 语言之父 Chris Lattner 宣布加入 Google, 让我想起前段时间 Android 大神 Jake Wharton 发推加入 Google 的消息。 传说中的 J 神 Jake Wharton J 神加入 Google 的推 Swift 语言之父 Chris Lattner 就在昨天 Chris Lattner 发推表示加入 Google ,专攻机器学习和人工智能。 Google 还是那么牛X,相信未来的 Android 会超过 IOS 做的更好。 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"学习英语的重要性","slug":"170714-学习英语的重要性","date":"2017-07-18T05:30:28.000Z","updated":"2017-07-19T13:08:49.654Z","comments":true,"path":"170714-学习英语的重要性.html","link":"","permalink":"https://dddreams.github.io/170714-学习英语的重要性.html","excerpt":"","text":"没有一个大学,是比拥有我们从未使用过的能力的大自我和人类意志与理智所创造的现实,更能包罗万象的了 one今天给大家分享一个老生常谈的话题 [ 英语 ],不知道大家是什么时候开始学英语的,我记得我是在小学五年级就开始学英语了,算下来也有十几年了吧,然而自己还是一个英盲。 two为什么说学习英语的重要性呢,前段时间跟着帅比张[stormzhang]领略了 google IO 大会,后来找了视频看了,结果一脸懵逼,听不懂人家在说什么,顿时觉得 low 爆了。另外,自己平时也经常刷 github 和 stackoverflow ,github 相对来说简单一些,看代码什么的也还能看懂一些,但是 stackoverflow 上的好多答案,着实让人头疼,看问题就是自己想要的结果可就是看不懂答案,也是一脸懵逼。记得上次培训时,老师说过一句话,中国是一个制造大国,而不是一个创造大国,很多有创意的产品都源于国外,因此想要看最原始的一手资料,那还是官方文档,但是很多官方文档都是英文的[再次懵逼]。 three那么要说学习英语,还是要从日常学起,浏览网页,推荐一款 chrome 插件,有道翻译,可以划词翻译,对于浏览英文网站很有帮助。昨天帅比张[stormzhang]还在公众号上发表一篇文章发现三个很赞的英语学习网站,推荐了三个学习英语的网站,大家可以去专注看看。我呢,今天也给大家一款 app ,用它来学习英语,也是很不错的,叫做 [HelloTalk] 的社交软件,你可以添加外国友人一起聊天,可以和一些也正在学习英语的人一起学习,可以练习口语等,可以发表动态,而且自带翻译,不过每天翻译次数有限,最让我看好的是别人可以指出你的动态中错误的语法并指正,这样学习起来不觉得枯燥,感觉挺好的,有需要的可以自己搜索下载哦。 four最近有了一个不错的想法,也是与英语有关,想做一个 app,但是学习成本有点大,于是就转战了微信小程序了,近期也正在学习,微信小程序的开发,具体是什么我就不透漏了,等做好了会关联在我的公众号上,到时候大家可以多多使用。 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"一周美图分享","slug":"170713-一周美图分享","date":"2017-07-13T11:27:38.000Z","updated":"2017-07-14T05:12:55.367Z","comments":true,"path":"170713-一周美图分享.html","link":"","permalink":"https://dddreams.github.io/170713-一周美图分享.html","excerpt":"","text":"已经有好长时间没写了,一是最近工作比较忙,每天加班;二是自己有一些事,以至于周末也没有更新。近期也没有什么好的内容去分享,今天来分享一个好玩的小程序。 先上一组图 不错这是一组图片生成的长图,今天分享的就是生成长图的小程序 [微软小蜜],看这个名字就知道很不错了吧,微软的产品,不会差到哪去,有兴趣的伙伴们可以在微信小程序中搜索,至于使用方法简单的不能在简单了,请自行使用吧。 ps:图片来源与必应搜索首页,纯属爱好不做商业使用。 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"Photo","slug":"Photo","permalink":"https://dddreams.github.io/tags/Photo/"}],"keywords":[]},{"title":"在 Windows 上使用 Ubuntu 是一种怎么的体验","slug":"170620-在-Windows-系统上使用-Ubuntu-是种怎么的体验","date":"2017-06-20T07:15:13.000Z","updated":"2017-06-20T09:05:33.474Z","comments":true,"path":"170620-在-Windows-系统上使用-Ubuntu-是种怎么的体验.html","link":"","permalink":"https://dddreams.github.io/170620-在-Windows-系统上使用-Ubuntu-是种怎么的体验.html","excerpt":"","text":"自从 Windows 10 问世以来,大家对 Windows 的吐槽变得没那么多了,不过说真的 Windows 10 的却改变了不少,扁平化的界面,全新的开始菜单,智能助手小娜,内置的应用商店,以及 Edge 浏览器,这些都让用户觉得耳目一新。虽然还有很多使用 Mac 的土豪用户和一些用着 Windows 还骂着 Windows 的吃瓜群众在吐槽,但是不得不说微软还是那个世界上科技技术的领头羊。 Bash on ubuntu on Windows今天来说说 Win10 内置的一个屌炸天的功能,Windows 上使用 Ubuntu 系统,这是 Windows 上为开发人员提供的 Bash shell 和 linux 环境,你可以直接在 Windows 上运行大多数 Linux 命令行工具,而告别臃肿的 VM。它可以使用 Linux 兼容的文件系统和层次结构,来访问固定的 Windows 存储,可以使用 vim, tmux 等工具,可以使用 javascript,node.js,Ruby,Python,C/C++等编程语言。看到这里是不是有一种马上要体验一下的冲动,好吧,让我带你装逼带你飞吧! 系统要求首先安装 Bash 对系统的要求: 你的系统必须是 64 为的操作系统 你的 Windows 版本必须 >= 14393 (如果版本低于 14393 请检查更新) 简单设置打开开发人员模式,系统设置 –> 更新和那全 –> 针对开发人员 –> 选中开发人员模式 启用Windows Subsystem for Linux功能,在开始菜单上右击 –> 搜索 –> 启用或关闭 Windows 功能 –> 勾选 Windows Subsystem for Linux (beta) 安装以管理员身份打开 PowerShell 运行下面命令1Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux 系统会提示重启系统,点击是,重新启动。 启动之后,打开命令行工具,运行 bash 命令 接受许可后,将下载Ubuntu用户模式映像,并在开始菜单中添加“Bash on Ubuntu on Windows”快捷方式。 至此,Bash on ubuntu on Windows 就安装好了,你就可以在 Windows 系统上尽情的玩耍 Ubuntu 系统了。 注意:你第一次安装 Bash on Windows 的时候,会被提示设置一个用户名密码,这个用户名密码是 Ubuntu使用的,和 Windows 系统的用户名密码没有任何关系。 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"微信,你还不知道的那些逆天功能","slug":"170618-微信,你还不知道的那些逆天功能","date":"2017-06-18T09:19:13.000Z","updated":"2017-06-20T06:29:45.120Z","comments":true,"path":"170618-微信,你还不知道的那些逆天功能.html","link":"","permalink":"https://dddreams.github.io/170618-微信,你还不知道的那些逆天功能.html","excerpt":"","text":"今天是父亲节,祝愿我们的父母身体安康,生活美满,也祝在我圈内的准父亲们节日快乐。借着今天这个节日,来扯扯微信那些逆天的功能。 小程序我想这个应该不用介绍了吧,从今年年初就已经火爆了整个互联网,虽然微信一直在很低调的更新完善着小程序,但是也已经火的一塌糊涂了。不过遗憾的是,我发现还有好多人的微信“发现”中没有小程序的入口,如果说是平常人,也就不说什么了,可是我发现的是我的几个同事,算得上是业内人士了,这就让人有点尴尬了,所以我觉得有义务给大家安利一哈了。 看下面的截图,如果在微信的“发现”中没有小程序,那么说明你还没有使用过小程序那么如何开启小程序呢,很简单扫一扫下面的二维码,打开微信官方的辟谣小程序,退出去再看,小程序的入口就有了。这个也是今天我要推荐的小程序,它是微信官方的辟谣小程序,你可以通过关键字搜索看有哪些文章是谣言,也可以你阅读的谣言次数,有了它妈妈再也不会担心你阅读的文章是不是真的了。 搜一搜 & 看一看搜一搜,看一看是微信的实验室功能,看下面的图,如果你的微信“发现”中没有搜一搜和看一看的标签,那么说明你还没有开启微信实验室功能。开启搜一搜,看一看的功能在微信 【设置】–【实验室】–【搜一搜】/【看一看】小伙伴们可以选择开启。是不是顿时觉得微信要逆天了,这是想抢百度,头条的饭碗啊。 通过微信读书阅读公众号文章有没有遇到这种情况,你正在读公众号文章,突然来微信消息了,你要点击返回到消息界面,回复完消息,然后再找到刚才阅读了的公众号,打开继续阅读,虽然微信做了优化,重新打开时定位在了刚才阅读的位置,可是消息又来了,又返回,又要找公众号,是不是很烦人,如果你也是一个公众号党,而且经常也遇到这种烦人的情况,那么告诉你,你的福音来了。我们可以通过微信读书来阅读微信公众号文章,微信读书相信大家并不陌生吧,在众多读书类软件中,微信读书可以算是佼佼者,最近又加入了声音阅读,那简直是又向完美更进了一步啊。闲话少说,来看看微信读书是怎么阅读公众号文章的.看到了吧,打开公众号文章,调出分享的上划菜单,在微信阅读中阅读,然后打开微信阅读,在书架中就有公众号阅读记录了。虽然这个功能还有很多不完善的地方,相信微信团队会进一步优化,达到你想要的便捷。 保存你的聊天记录当今的微信,已经是人们工作生活的必需品,相信很多人都将它作为工作中交流的工具,所以有时候简短的聊天记录就变的非常重要了,但是我们都知道微信的聊天记录是有期限的,会定期的删除以前的记录,为了保存重要的记录,我们来借助印象笔记保存。为什么选择印象笔记呢,因为印象笔记有独立的邮箱。其实微信收藏也可以保存聊天记录,只是微信收藏消息是一条一条收藏的,不利于日后查看。然而印象笔记可以保存多条在一篇日记中,同时也保存了聊天人的昵称和时间。 用印象笔记保存聊天记录需要几个前提条件: 微信绑定印象笔记 QQ与微信绑定 (由于要用到 QQ 邮箱) 将印象笔记独立邮箱账号添加至 QQ 邮箱的通讯录中万事俱备,只欠如何操作了,在聊天界面中长按消息,点击【更多】然后选中你要保存记录,点击分享按钮调出上划菜单,选择【添加至邮箱】,最后选择收件人为你印象笔记的独立邮箱账号,点击发送,就可以在印象笔记中查看你的聊天记录了。 get 到新姿势的小伙伴们,赶快去试试吧 -_– 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"微信","slug":"微信","permalink":"https://dddreams.github.io/tags/微信/"}],"keywords":[]},{"title":"AI时代,真正体现人类智慧的时代","slug":"170604-AI时代,真正体现人类智慧的时代","date":"2017-06-13T09:28:21.000Z","updated":"2017-06-14T02:43:13.928Z","comments":true,"path":"170604-AI时代,真正体现人类智慧的时代.html","link":"","permalink":"https://dddreams.github.io/170604-AI时代,真正体现人类智慧的时代.html","excerpt":"","text":"早在 2014 年 5 月微软发布了一款智能伴侣虚拟机器人,名叫“微软小冰”,她可以和你聊天互动,并且有提醒,百科,天气,交通等功能,从那时起人工智能渐渐进入了普通人的生活,后来相继出现了图灵机器人、小黄鸡还有百度推出的“度秘”等等,这些仅仅是人工智能的一小部分。 前段时间人们一直关注的 Google AlphaGo 与围棋世界冠军何洁的对战,AlphaGo 4:0 战胜何洁,据说战胜何洁的 AlphaGo 比去年战胜李世石的 AlphaGo 有着根本的不同,比去年的 AlphaGo “强三子”,“强三子”是什么意思呢,就是说让对方先落三子,可想而知 AlphaGo 在一年内的成长是惊人的,使用的算法更优,计算速度更快。在这个大数据的时代,计算速度预示着人们能够处理超大数据量的能力,而且新一代的 AlphaGo 不在使用 3000万的棋谱进行训练,要知道优质的数据是机器学习不可或缺的核心条件,然而 Google 就这么放弃了,而且计算效率更快,这就是我一直崇尚的 Google 。 AlphaGo 是如何决定落子的。也许大家都在疑惑,它是如何决定落子的,其实棋局的信息相当于输入信息,AlphaGo 会根据策略网络探索哪个位置同时具备高潜在价值和高可能性,进而决定最佳落子位置。在分配的搜索时间结束时,模拟过程中被系统最频繁考察的位置将成为 AlphaGo 的最终选择。在经过先期的全盘探索和过程中对最佳落子的不断揣摩后,AlphaGo的搜索算法就能在其计算能力之上加入近似人类的直觉判断。 同时 AI 也是 Google IO 大会的另一个主题,Google 发布的集成了 Google Assistant 的 Google Home ,一款居家的智能音响,说是参加了今年的 Google IO 大会的人都送了一个 Google Home,让我心动了好久,但是参加 Google IO 大会可真是有困难的,不说要花费多少,就算有钱也不一定可以参加。 没过多久,苹果公司开了 WWDC 大会,同样也发布了一款叫做 HomePod 的智能音响,用户可以利用“Hey Siri”唤醒HomePod,获取天气、新闻和交通等热点信息,也可以利用语音指令控制连接到HomePod上的其它 HomeKit 智能家居产品,当然你也可以像其它智能音箱一样,搜索查询需要的内容。 所以我说 AI 时代,是真正体现人类智慧的时代,未来的机器将会像人一样思考,学习,或许像人类一样有感情也不是没有可能,因为真正的智慧是人类创造的。 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"听说Kotlin比Java更优雅","slug":"170519-听说Kotlin比Java更优雅","date":"2017-05-19T03:16:31.000Z","updated":"2017-05-19T08:15:02.797Z","comments":true,"path":"170519-听说Kotlin比Java更优雅.html","link":"","permalink":"https://dddreams.github.io/170519-听说Kotlin比Java更优雅.html","excerpt":"","text":"一夜之间 Kotlin 变得比 Java 更优雅了,也许是自己太 low 了,之前还没听过 Kotlin 这个语言呢。然而,从昨天开始 Kotlin 的教程、文档、博客就在各大论坛火热了起来,原因是 18 号的 Google IO 大会,大会的亮点我就在不罗嗦了,微博、公众号上都已经传遍了,Kotlin 就是其中的亮点之一,Google 把 Kotlin 作为 Android 开发的官方语言,并在 Android Studio 3.0 默认集成 Kotlin plug-in。在我看来 Kotlin 有 Google,Android 的撑腰,相信不会死的很快。 那么,我们就来看看 Kotlin 是不是比 Java 更优雅呢 Kotlin 是一个基于 JVM 的编程语言,由 JetBrains 开发,它可以编译成 Java 字节码,也可以编译成 JavaScript ,方便在没有 JVM 的设备上运行,是一种兼容 Java 的语言,类似于 Scala ,曾经有人试图用 Scala 来开发 Android 应用,但是确实非常头疼,因为你需要发布运行库。但是在 Android 的世界里,Kotlin 已经不是什么新鲜事了,早先就有人预测 Kotlin 将会是 Android 的主流开发语言,不料,现在已经实现了。 优雅的 Hello World123fun main(args: Array<String>){ println(\"Hello World!\")} 看到了没,没有 class ,没有 static 的 main 方法,自定义一个求和方法 sum123fun sum(a: Int, b: Int): Int{ return a + b} 还有一种简写的形式1fun sum(a: Int, b: Int) = a + b 是不是看上去很优雅,调用 sum 方法123456fun main(args: Array<String>){ println(sum(3, 5)) // 输出 8 // 还可以这样写 println(\"sum of 3 and 5 is ${sum(3, 5)}\") // 输出 sum of 3 and 5 is 8} 机智的 boy 已经发现,代码不以分号结尾,变量类型和方法返回值类型在后面,还有类似 JavaScript 的字符串模板等等。是的,Kotlin 的语法借鉴了很多函数式编程语言的特性,你会发现它和 Go,swift 语言也很相似,这说明 Kotlin 也借鉴了众多新诞生的编程语言的新特性,再加上 Google 的支持,很显然它将是未来主流。 另外,Kotlin 和 Java 完全兼容,这也可能是 Google 选择 Kotlin 的原因之一,它可以完全兼容 Java ,对于旧的应用而言就不用花费时间精力去将应用之前的代码转换为 Kotlin 了,毕竟替换旧代码也是一个非常浩大的工程。 最后附上 Kotlin 的官方中文教程 http://www.kotlincn.net/docs/reference/ 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"Kotlin","slug":"Kotlin","permalink":"https://dddreams.github.io/tags/Kotlin/"},{"name":"Google","slug":"Google","permalink":"https://dddreams.github.io/tags/Google/"}],"keywords":[]},{"title":"以正确的姿势庆祝母亲节","slug":"170514-以正确的姿势庆祝母亲节","date":"2017-05-14T10:03:01.000Z","updated":"2017-05-19T08:15:53.243Z","comments":true,"path":"170514-以正确的姿势庆祝母亲节.html","link":"","permalink":"https://dddreams.github.io/170514-以正确的姿势庆祝母亲节.html","excerpt":"","text":"今天是母亲节,祝天下母亲节日快乐,也祝自己母亲身体健健康康,平平安安的,在这我也想告诉您,您牵挂的孩子们都好,勿念! 小时候不知道有这个节日,也不知道是什么时候知道的,记得好像是有了手机,有了互联网,这个节日便在国内盛行了起来。每当这个节日的时候网络上都会掀起一波母亲热,今年也是一样,万能的朋友圈已经无力吐槽,不管是出于什么目的,让人们记住这个节日也不算是坏事。 说起手机,尤其是近几年智能手机几乎是覆盖了所有人,也包括我们的父母在内。今年过年的时候,父亲抱着手机让我外甥教他怎么玩微信,怎么聊天,我也给他教怎么安装软件,怎么下载电影。以前父亲特别爱看电视,几乎每天下班都趴在电视前面,现在倒好,学起了年轻人,成了低头族。 父亲经常抱怨,手机用着用着就卡了,或者是花费刚交上就没了,我看他手机才发现,安装了 N 多个不知名的软件,而且同时开启着很多后台应用,他说他也不知道怎么就安装上了,更糟糕的是消息栏中,各种信息,非常 6+1 中奖的,胡汉三搞笑小品,某某村儿童走丢的,央求转发,某地感染H7N9的,提醒鸡肉不能吃了的,应有尽有。不错,信息时代,信息很重要,然而垃圾信息实在太多了。对于我们的父母,能否识别信息的真伪,这个很难说;接到诈骗短信给打钱的,接到中奖短信预支付的不是没有发生过。 所以,我想说的是,借这个母亲节,给咱们父母提个醒: 1、如果接到涉及人身安全,及钱财的短信或者陌生电话,务必打电话确认是否真实,并且一定要在确认真实以后再做后续事项(比如打钱等)。2、如果接到中奖、某某膏药能治百病类似这样的短信或者消息,直接忽略,并将其拉黑。3、如果接到急救、求助、借钱的短信或者陌生电话,务必打电话确认是否是自己的亲人或者朋友,再做后续事项。4、如果看到感染H7N9病毒,禽流感之类的,不必惊慌,看看电视新闻有没有报道,如果没有直接忽略。5、如果看到搞笑视频,敏感话题之类的,建议不用看,一般都是标题党(利用醒目的标题来赚取用户的点击量)6、如果在某消息中包含卸载链接,不要点,一般都会是垃圾软件包,或者病毒文件,或者某某炫酷的网站(骗取用户点击量)7、对于新闻的真伪,要以电视新闻节目为准,或者正规的新闻机构为准,不要完全相信别人转发或者某某头条推送的新闻。8、下载软件,要在正规的软件市场,下载正规的有一定知名度的软件。9、看电影,听音乐也要在正规的有一定知名度的官方 APP 上。 目前就想到这么多,若大家有其他想提醒的事项,可以在我微信上私信给我。也可以转发给自己的父母,以后遇到如上的信息,可以辨别信息是否虚假,以防上当受骗。 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"信息时代,网络威胁离我们很近","slug":"170513-信息时代,网络威胁离我们很近","date":"2017-05-13T08:28:49.000Z","updated":"2017-05-19T08:21:19.455Z","comments":true,"path":"170513-信息时代,网络威胁离我们很近.html","link":"","permalink":"https://dddreams.github.io/170513-信息时代,网络威胁离我们很近.html","excerpt":"","text":"前言最近一直比较忙,公司派去出差,在火车上晃荡了几天,感觉有些累,因此这段时间一直没有更新文章,幸好这周双休有时间来写写。首先要感谢在上期文章中赞赏的小伙伴,谢谢你的支持,虽然我不知道是谁;正是处于这个原因,我决定将付款的二维码去掉,并坚持写原创文章来获得微信公众号留言赞赏的功能。 今天的话题为什么要说网络安全呢,大家可能都觉得这个话题离自己很远,其实不然,就在昨天,国内各地许多高校以及很多医院中一些联网的电脑上突然出现了黑屏,屏幕上显示着一些字,大概的意思是:“你大可在网上找找恢复文件的方法,我敢保证,没有我们的解密服务,就算老天爷来了也不能恢复这些文件”,并给出了解决方案:“你需要给我们支付300美元等值的比特币,我们就会帮你解锁你的电脑,如果你不在三天内付款,那么你的解锁费用将会翻倍”,而且对于一些医疗机构索要的不仅仅是300美元的比特币,而是300比特币,约合350万人民币。据报道这次的网络攻击在全球内都有发生,包括英国,俄罗斯等国,并且还在不断扩散。如果在你电脑上出现了类似以上的情况,那么恭喜你,你不幸中标了。更不幸的是,对于黑客这样明目张胆的勒索,专家表示对于已经中毒的电脑,目前没有解决办法,而对于没有中毒的用户表示庆幸的同时,该对你的电脑加强防护了。 电脑加强防护在我身边的很多人,使用电脑的不在少数,但是懂得电脑防护和正确使用的人没有几个,今天我就列几个简单的电脑防护及使用电脑的方法: 1、安装杀毒软件,比如: avast!(来自捷克,中文名为爱维士,已有数十年的历史,它在国外市场一直处于领先地位) Microsoft Security Essentials(微软自家的杀毒软件,占用资源较少,win10 自带) ESET NOD32(由ESET发明设计的杀毒防毒软件,是一个全球性的安全防范软件公司,主要为企业和个人消费者提供服务) Avira(一套由德国的Avira公司所开发的杀毒软件,中文名小红伞,一款知名的免费杀毒软件,用户超过七千万,在系统扫描、即时防护、自动更新等方面,表现都不输给知名的付费杀毒软件) 金山毒霸(中国的反病毒软件) 以上这几个杀毒软件是业界比较出名的,也是非常靠谱的,而且都有免费的版本。再不行安装个某某管家,某某安全卫士之类的,也比电脑裸奔的强。 2、Windows 系统会定期的发布系统补丁和漏洞,建议及时更新系统补丁。 3、下载软件、视频、音乐等文件尽量去官网下载。一般很多病毒都是附在一些不正规的软件或视频上的,用户只要下载了,就会激活病毒从而感染你的电脑。 4、.exe 的可执行文件,大小在 1M 以内要特别注意,一般都是捆绑了很多垃圾软件,只要你双击安装,就会给你莫名其妙的安装 N 多个软件。 总结信息时代,网络威胁其实离我们很近,越来越多的网络终端设备,智能硬件等都已经离不开网络,尤其是一些医疗设备,银行终端,一旦受病毒感染,那么威胁的不仅仅是我们的财产,而且也会威胁到人们生命,因此,网络安全是一个不得不重视的问题,在这呢我也提醒大家,提高网络安全意识,养成良好的上网习惯,我们的网络环境还是很安全的。 扯淡完毕 ~_~~ 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"推荐自己的公众号","slug":"推荐自己的公众号","date":"2017-05-02T10:07:42.000Z","updated":"2017-05-19T08:17:44.614Z","comments":true,"path":"推荐自己的公众号.html","link":"","permalink":"https://dddreams.github.io/推荐自己的公众号.html","excerpt":"","text":"没错,不用怀疑自己的眼睛,就是推荐自己的公众号,为自己打打广告,没办法啊关注的人太少,微信不给开留言评论功能,所以只能打广告拉人了,,, 1就在前几天苹果爸爸出新规定,导致微信关闭了IOS版微信公众号的赞赏功能,没办法啊,就连企鹅帝国也得妥协,微信官方给出提醒在阅读完公众号文章后,可以通过二维码转账的方式对公众号表示支持,这倒是给了我一个很好的启发,就是贴上付款二维码,就算微信不给我开留言评论赞赏功能也可以赞赏,因此在上一篇文章的最后我就贴上了转账二维码(温馨提示:最后贴的是转账二维码,大家看清楚想明白在扫);更加让我意外的是尽然有人赞赏了,我兴冲冲的想看看是谁赞赏的,但是找了半天没找到付款的是谁,这就尴尬了,或许是我自己没找对地方还是什么原因,要是有哪位小伙伴知道的可以给我微信上说一哈。不过后来我也猜到是谁赞赏的了,估计她的赞赏不是因为文章写的好,而是由于特殊关系而赞赏的吧,在这我也提醒大家,赞赏是对优质文章的赞赏,是对作者的鼓励,要是觉得文章对你没什么用,就不用赞赏了。 2对于苹果禁止公众号“赞赏”功能,网上也众说纷纭,其中有种说法是,自从微信低调的推出小程序后,又紧接着开放个人开发者申请小程序,公众号关联小程序,公众号文章支持添加小程序等,这将公众号和小程序之间实现了无缝对接。目前已经有很多公司率先推出小程序,比如最近很火的摩拜单车,据悉,摩拜单车每周的使用量实现100%的增长,新用户提升了两倍,这些新用户几乎都来自小程序。不得不说小程序的出现引发了很多创业者的担忧,有人认为,小程序开放能力越强,其与苹果在流量渠道的竞争遍愈发激烈,关停赞赏功能只是一个表面现象,背后是生态控制力和入口的争端。 3早在今年2月份就有人爆出,微信或将推出付费阅读,或者说付费阅读将是一个趋势,如今碎片化阅读,有质量有价值的内容越来越少,付费阅读是一个好的趋势,像小密圈的崛起,就是一个很好的诠释,所以苹果的这个做法矛头有点指向付费阅读。 4不管这些破事,还是给自己公众号做好广告,希望小伙伴们多多关注,我将更加努力写出更有价值的文章-_– 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"微信公众号推荐","slug":"微信公众号推荐","date":"2017-04-22T10:18:59.000Z","updated":"2017-04-25T13:21:14.626Z","comments":true,"path":"微信公众号推荐.html","link":"","permalink":"https://dddreams.github.io/微信公众号推荐.html","excerpt":"","text":"起因早在2015年刚参加工作的时候,这个公众号就已经存在了,当时还不叫这个名字,本来是想做一个问答的公众号,最初集成了一些开放的 API 接口,提供了一些常用的服务,比如:天气,快递,火车票等的查询,但是由于种种,放弃了这种想法,因此这些服务都已经下线。现在只是一个类似与博客的公众号,平时写写文章、总结一些经验、积累一些技术等。它提供了一个写作的平台,我是喜欢写一些东西的,只是工作之后时间越来越少,没有坚持一直写,还有一个原因就是坚持不了,每次写的时候都感觉没什么可写,这也是很多童鞋在运营公众号的时候遇到的问题,坚持不下来。 运营期间我关注了很多公众号,想借鉴借鉴别人是怎么坚持的,也有一些技术类公众号,碎片化阅读,公众号是一个不错的选择。今天所推荐的这些公众号我几乎每天都会看的,所以推荐给大家。说明一下,绝不是为别人做广告,只是觉得他们的文章不错,仅此而已,,, AndroidDeveloperID: googdev人称帅比张,也许是真的很帅吧,我没见过,不过他的文章的确写的很不错,有很多独到的见解,是一个 Android 开发者,非科班出身,也是我一直坚持写文章的动力来源,他写了很多关于他自己是如何坚持写博客的文章,也写了很多阅读写作技巧,是一个值得关注的公众号。不过最近他开了个付费的小密圈,只是我没有加,听说已经有超过一千人加入了,在那里可以进行交流,提问等,貌似还不错。 谷歌开发者这是 google 的官方公众号,发布一些最新的 google 动态,喜欢 google 的小伙伴可以关注。 鬼脚七前淘宝技术专家,虽然不是每天发布文章,但是他的文章也是非常值得阅读的,分享很多技术及职业发展的文章,也是值得关注的公众号。 野狗野狗官方的公众号,野狗这个公司我了解的不多,只知道是一个提供可靠的实时通讯云服务公司,在这个公众号经常可以看到很多关于前端技术的文章和教程,写的相当不错,包括一些前端框架,微信小程序相关的文章,喜欢钻研的小伙伴动手关注,相信你会找到不少干货的。 前端之巅这也是一个不错的公众号,常常发布一些前端最新动态,了解最新框架发布动态,就靠它了。 infoQinfoQ 官方公众号,infoQ是一个良心公司,本着开源的理念,定期组织开发者大会,都是业界大牛,我看过几次贺师俊老师的演讲,那叫一个精彩啊,其中一次是对 javascript 语言的展望,讲的非常生动,如果你看了相信你会爱上 javascript 这门语言的,目前就职于百姓网,有名的前端工程师。在这个公众号上你可以经常见到这样的大牛,还有每月会发布架构师专栏的电子书,也是非常不错的书籍。 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"一个对教师的话题","slug":"一个对教师职业的话题","date":"2017-04-22T07:22:13.000Z","updated":"2017-04-22T09:27:40.379Z","comments":true,"path":"一个对教师职业的话题.html","link":"","permalink":"https://dddreams.github.io/一个对教师职业的话题.html","excerpt":"","text":"前言教师这个职业一直以来都是受人尊重,圣神而又高尚的职业,今天的这篇文章是因我一位朋友而起,文章内容也只代表我个人看法,不是针对某个人或某个团体。事情的起因是这样的:今天在朋友圈看到一条心情,还附有一张截图,内容大概是这样的,有一位学生家长给老师发消息说,自己的孩子英语单词会写也认识就是不知道怎么读,怎么发音,因此建议老师给学生教一下音标,因为自己当时在学英语的时候老师就教的是音标。所以我这位朋友因此发心情说,三年级的学生连字母都没认全,还教音标,真是胡扯蛋。。。 引发思考三年级的学生应不应该教音标教学大纲基本上是这样说的,能听懂会说12组对话,并能进行简单的交流;能听说认读50个单词和听说读写26个字母,并能简单运用;(1)基本能够听懂课堂用语。(2)能够按顺序背诵字母表,并可以认读、拼写字母的大小写形式,区分印刷体和手写体。(3)能够用正确的语音、语调朗读所学的单词、词组和课文。(4)能够根据场景,用正确的语音、语调进行简单的对话。(5)能够听懂,并用正确的语音、语调说出所学单词400个左右,并能够拼写其中所学常用单词200个左右。(6)能够正确、规范地书写大小写字母、单词和句子。的确是没有提及音标的教学。所以我个人认为低年级的教学应该按照教学大纲来开展,因为教学大纲是根据大部分学生的心理发展安排的,不会有什么问题,就算有个别的学生有这样的需求,也要以大部分学生的发展为主。 学生家长应不应该有这样的建议对于学生家长,父母的心情是可以理解的,自古以来父母望子成龙,望女成凤的愿望尤其强烈,所以,家长有这样的建议也是想让自己的孩子能更好的接受知识,只是家长没有站在老师的角度考虑问题,一个班的学生肯定是有差异的,不可能为了一个孩子就改变老师的教学方案,这样做显然是不合理的,家长这样的建议是自私的表现,但是这样是自私我们是完全可以理解的。 老师该不该说家长是胡扯首先对“胡扯”一词怎么看,它是一个多义词,也没有贬义的意思,所以也可以这么说,但是作为一名教师,对于学生家长的建议,应该虚心听取,即使家长的建议是错误的,也要解释清楚不那么做的原因,用论证的思想让别人认识清楚,这样就可以很好的解决问题;而不是不假思索的说别人的建议是胡扯,在这点上老师是不对的。 老师的职业道德记得还是在小学的时候,我的一位老师曾说过,教书育人,这个词应该是反过来说的,先有育人才有教书,如果连做人的有问题,那么学习再好将来也会对社会造成不良影响。老师的这句话很有道理,从而至今我也还记得。故人说:“师者,传道授业解惑也”,而正真的“师者”,绝不是仅仅只为了“传道授业解惑”,还要在“为人”、“处事”上进行言传身教,所谓的“言传身教”,当今的老师又有几个做到了,前段时间一度网络上爆出某某学校校长猥亵学生、某某幼儿园教师体罚幼儿种种,这些都表明当今的教师团体存在一些超出道德范畴的行为,值得我们深思。 结论教师的职业道德一直以来都是被人们广泛关注的话题,也希望广大教育工作者能以身作则,为教育事业做出贡献,不要让教师这个职业因为一只苍蝇而影响了人们心中圣神而又高尚的象征。 其实我只是在扯淡 -_– 更多文章请关注微信公众号: zhiheng博客 如果觉得文章对你有用,小额赞赏是对作者莫大的支持,也是作者坚持写作的动力-_–","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"简单易用的 HtmlEncode","slug":"简单易用的-HtmlEncode","date":"2017-04-07T02:50:58.000Z","updated":"2017-05-19T08:21:00.638Z","comments":true,"path":"简单易用的-HtmlEncode.html","link":"","permalink":"https://dddreams.github.io/简单易用的-HtmlEncode.html","excerpt":"","text":"前言最近在项目中遇到漏洞检查的问题,其中就有 script 脚本注入的问题,简单的说 script 脚本注入就是在接收用户输入时,将 javascript 脚本提交到服务器了,这样在显示时浏览器就会执行这些脚本,从而进行攻击,常见的有跨站脚本 (XSS) 攻击等。 解决方案其实解决方案也很简单,就是将其用户的输入信息进行 html 编码,将特殊字符转换为 html 实体,这样就能可以简单的防止了 script 注入的攻击,但是对于业界高手,还要进一步加强安全策略。 服务端转义可以在服务器端对用户的输入信息进行编码:123456789101112131415161718192021222324// java 提供的 APIString message = Html.Encode(message);// 简单实现public class HtmlEncode { public static void main (String[] args){ String s = new String(\"<script>alert(1);</script>\"); String str = encodeHtml(s); System.out.println(str); // &#60;script&#62;alert(1);&#60;/script&#62; } public static String encodeHtml(String s){ StringBuffer out = new StringBuffer(); for(int i = 0; i < s.length(); i++){ char c = s.charAt(i); if(c > 127 || c=='\"' || c=='<' || c=='>') { out.append(\"&#\" + (int)c + \";\"); } else { out.append(c); } } return out.toString(); }} js 前端转义同样也可以在浏览器端进行转义:123456789101112131415161718192021222324(function(){ 'use strict'; var root = typeof window === 'object' ? window : {}; var htmlEncode = function(value, entry) { if(!entry){ entry = { \"'\": \"&apos;\", '\"': '&quot;', '<': '&lt;', '>': '&gt;' }; } if(value){ value = value.replace(/(['\")-><&\\\\\\/\\.])/g, function ($0) { return entry[$0] || $0; }); } return value; } if(root){ root.htmlEncode = htmlEncode; }})();var message = '<script>alert(1);</script>';message = window.htmlEncode(message); // \"&lt;script&gt;alert(1)&lt;/script&gt;\"// 在浏览器中显示 <script>alert(1)</script> 总结这两种方法简单实用,基本可以解决简单的脚本注入问题。 更多文章请关注微信公众号: zhiheng博客","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"https://dddreams.github.io/tags/Java/"},{"name":"JavaScript","slug":"JavaScript","permalink":"https://dddreams.github.io/tags/JavaScript/"}],"keywords":[]},{"title":"JavaScript全局函数eval的用法","slug":"JavaScript全局函数eval的用法","date":"2017-03-03T11:24:24.000Z","updated":"2017-05-19T08:19:32.014Z","comments":true,"path":"JavaScript全局函数eval的用法.html","link":"","permalink":"https://dddreams.github.io/JavaScript全局函数eval的用法.html","excerpt":"","text":"前言最近在项目重构代码时发现很多地方用到 eval() 这个函数,也不知是哪位大神写的神奇代码,于是乎就上网搜了一下关于 eval() 这个函数的用法,具体整理如下: 定义eval() 函数执行表示为字符串形式的JavaScript代码。 语法1eval(string) 参数 string一个字符串表示了一个JavaScript运算式,语句, 或者是一系列语句。运算式可以包括变量和已存在对象的属性。 返回值 执行指定的代码之后的完整值。如果完整值为空,返回undefined 说明eval()是全局对象的一个函数属性。 eval()的参数是一个字符串。如果字符串表示了一个运算式,eval()会对运算式求值。如果参数表示了一个或多个JavaScript语句, 那么eval()会执行这些语句。不要调用eval()来执行一个四则运算运算式; JavaScript 会自动为四则运算求值。 12eval(new String(\"2 + 2\")); // 返回了包含\"2 + 2\"的字符串对象eval(\"2 + 2\"); // returns 4 该方法只接受原始字符串作为参数,如果 string 参数不是原始字符串,那么该方法将不作任何改变地返回。因此请不要为 eval() 函数传递 String 对象来作为参数。 不过可以使用 toString() 方法来绕过这个限制12var str = new String(\"2 + 2\");eval(str.toString()); // 4 如果试图覆盖 eval 属性或把 eval() 方法赋予另一个属性,并通过该属性调用它,则 ECMAScript 实现允许抛出一个 EvalError 异常。 避免在不必要的情况下使用evaleval() 是一个危险的函数, 他执行的代码拥有着执行者的权利。如果你运行eval()伴随着字符串,那么你的代码可能被恶意方(不怀好意的人)影响, 通过在使用方的机器上使用恶意代码,可能让你失去在网页或者扩展程序上的权限。更重要的是,第三方代码可以看到作用域在某一个eval()被调用的时候,这有可能导致一些不同方式的攻击。相似的Function就是不容易被攻击的。 eval()也普遍的比其他的替代方案慢,因为他会调用js解析器,即便一些其他的构造器已经被做了优化在现代的JS引擎中。 更为安全(也更快)的替代eval()的是普通的用例。 JavaScript 为什么不推荐使用 eval() eval只是一个普通的函数,只不过他有一个快速通道通向编译器,可以将string变成可执行的代码。有类似功能的还有Function , setInterval 和 setTimeout。 eval不容易调试。用chromeDev等调试工具无法打断点调试,所以麻烦的东西也是不推荐使用的。 说到性能问题,在旧的浏览器中如果你使用了eval,性能会下降10倍。在现代浏览器中有两种编译模式:fast path和slow path。fast path是编译那些稳定和可预测(stable and predictable)的代码。而明显的,eval不可预测,所以将会使用slow path ,所以会慢。还有一个是,在使用类似于Closure Compiler等压缩(混淆)代码时,使用eval会报错。 关于安全性,我们经常听到eval是魔鬼,他会引起XSS攻击,实际上,如果我们对信息源有足够的把握时,eval并不会引起很大的安全问题。而且不光是eval,其他方式也可能引起安全问题。 比如:莫名其妙给你注入一个<script src="">标签,或者一段来历不明的JSON-P请求,再或者就是Ajax请求中的eval代码… 所以啊,只要你的信息源不安全,你的代码就不安全。不单单是因为eval引起的。你用eval的时候会在意XSS的问题,你越在意就越出问题,出的多了,eval就成噩梦了。 效率问题是程序逻辑问题。对于一些有执行字符串代码需求的程序中,不用eval而用其他方式模拟反而会带来更大的开销 总结因此,在实际项目中尽可能的避免使用 eval() ,除非特殊场景必须要使用。 更多文章请关注微信公众号: zhiheng博客 ps:其实我是在扯淡,,,,","categories":[],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://dddreams.github.io/tags/JavaScript/"}],"keywords":[]},{"title":"JavaScript模块化编程补充Sea.js","slug":"JavaScript模块化编程补充","date":"2017-02-21T02:54:34.000Z","updated":"2017-05-19T08:19:37.453Z","comments":true,"path":"JavaScript模块化编程补充.html","link":"","permalink":"https://dddreams.github.io/JavaScript模块化编程补充.html","excerpt":"","text":"官网的介绍:Sea.js 追求简单、自然的代码书写和组织方式,具有以下核心特性: 简单友好的模块定义规范:Sea.js 遵循 CMD 规范,可以像 Node.js 一般书写模块代码。 自然直观的代码组织方式:依赖的自动加载、配置的简洁清晰,可以让我们更多地享受编码的乐趣。 Sea.js 还提供常用插件,非常有助于开发调试和性能优化,并具有丰富的可扩展接口。 社区说: SeaJS 的理念的是 海纳百川、有容乃大,推崇开放与自由 下载 https://github.com/seajs/seajs/releases CMD模块规范在 Sea.js 中,所有 JavaScript 模块都遵循 CMD(Common Module Definition) 模块定义规范。该规范明确了模块的基本书写格式和基本交互规则 在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下:123define(function(require, exports, module){ doSomething...}); define 的参数有多种类型,可以是字符串、对象、函数等;还有 define 也可以传入多个参数:1234// define(id?, deps?, factory)define('hello',['jquery'], function(require, exports, module){ doSomething...}); 字符串 id 表示模块标识,数组 deps 是模块依赖,上面的写法是省略 id, deps 参数的写法,因此前两个参数是可以省略的。 注意:带 id, deps 参数的 define 用法不属于 CMD 规范,而属于 Modules/Transport 规范。 require, exports 和 module 三个参数的用法在下面 sea.js 的 API 中有所介绍。 常用APIseajs.config 用来对 Sea.js 进行配置。 123456789101112131415seajs.config({ // 设置路径,方便跨目录调用 paths: { 'arale': 'https://a.alipayobjects.com/arale', 'jquery': 'https://a.alipayobjects.com/jquery' }, // 设置别名,方便调用 alias: { 'class': 'arale/class/1.0.0/class', 'jquery': 'jquery/jquery/1.10.1/jquery' }}); seajs.use 用来在页面中加载一个或多个模块。 12345678910111213// 加载一个模块seajs.use('./a');// 加载一个模块,在加载完成时,执行回调seajs.use('./a', function(a) { a.doSomething();});// 加载多个模块,在加载完成时,执行回调seajs.use(['./a', './b'], function(a, b) { a.doSomething(); b.doSomething();}); define 用来定义模块。Sea.js 推崇一个模块一个文件,遵循统一的写法:12345define(function(require, exports, module) { // 模块代码}); require, exports 和 module 三个参数可酌情省略,具体用法如下。 require require 用来获取指定模块的接口。 12345678define(function(require) { // 获取模块 a 的接口 var a = require('./a'); // 调用模块 a 的方法 a.doSomething();}); 注意,require 只接受字符串直接量作为参数, require.async 用来在模块内部异步加载一个或多个模块。1234567891011121314define(function(require) { // 异步加载一个模块,在加载完成时,执行回调 require.async('./b', function(b) { b.doSomething(); }); // 异步加载多个模块,在加载完成时,执行回调 require.async(['./c', './d'], function(c, d) { c.doSomething(); d.doSomething(); });}); exports 用来在模块内部对外提供接口。 123456789define(function(require, exports) { // 对外提供 foo 属性 exports.foo = 'bar'; // 对外提供 doSomething 方法 exports.doSomething = function() {};}); module.exports 与 exports 类似,用来在模块内部对外提供接口。123456789define(function(require, exports, module) { // 对外提供接口 module.exports = { name: 'a', doSomething: function() {}; };}); 总结sea.js的优点 1,通过exports暴露接口。不需要命名空间,不需要全局变量。 2,通过require引入依赖。让依赖内置,开发者只关心当前模块的依赖,其他事情Seajs都会自动处理好。 其他好处: 1,模块的版本管理。通过别名配置,配合构建工具,可以比较轻松地实现模块的版本管理。 2,提高可以维护性。 3,前端性能优化。通过异步加载模块,对性能非常有益。 4,跨环境共享模块。 随着Dojo,YUI3,Node.js推广和流行,前端模块化才开始深入人心。 一类是大教堂模式。如Dojo,YUI3,KISSY等。所有组件都是颗粒化、模块化的,各组件之间层层分级、环环相扣。 一类是集市模式。如 jQuery、RequireJS、Sea.js、OzJS 等。所有组件彼此独立、职责单一,各组件通过组合松耦合在一起,协同完成开发。 sea.js缺点 1.文档很烂,官网只有一个很简单的例子,玉伯开了用Github issues 做文档的坏头,导致有的文档issue 里说的东西已经过时了,给人误导。 2.打包痛苦,配置起来极其复杂,而且一变再变,早期的transport/concat组件简直了,bug挺多。而且这个打包跟spm的关系到底是啥?!! 3.sea.js文件路径,模块路径,spm路径,打包路径,相对路径,绝对路径,html入口文件路径,这些关系在sea.js初期的时候开发团队没有理清楚,现在我也不知道seajs3和spm3搞清楚没。 4.每个小版本大版本都有很大变化,从seajs1到2到3,每次变化都挺大的,有些配置莫名其妙地就没了,也没有一个统一的文档来记录。 摘自知乎LABjs、RequireJS、SeaJS 哪个最好用?为什么? 鲁小夫的回答。 更多文章请关注微信公众号: zhiheng博客","categories":[],"tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"https://dddreams.github.io/tags/JavaScript/"}],"keywords":[]},{"title":"前端开发之JavaScript实战应用一","slug":"前端开发之JavaScript实战应用一","date":"2017-02-04T07:32:12.000Z","updated":"2017-05-19T08:20:48.994Z","comments":true,"path":"前端开发之JavaScript实战应用一.html","link":"","permalink":"https://dddreams.github.io/前端开发之JavaScript实战应用一.html","excerpt":"","text":"一、JavaScript 模块化编程原始写法1234567var conut = 0;function m1(){ //...}function m2(){ //...} 使用时直接调用,这种写法”污染”了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。 对象写法123456789var module1 = new Object({ count : 0, m1 : function (){ //... }, m2 : function (){ //... }}); 像这样封装在一个对象里面,使用时调用对象的属性。module1.m1(); 但是,这样的写法会暴露所有模块成员,内部状态可以被外部改写module1.count = 5; 立即执行函数写法123456789var module1 = (function(){ var count = 0; var m1 = function(){ console.log(‘comule1’); }; return { m1 : m1 };})(); 这样外部代码无法读取内部的count变量1console.log(module1 .count); // undefined 放大模式如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用”放大模式”12345678var module1 = (function (mod){ mod.m3 = function () { console.log('m3'); }; return mod;})(module1);Module1.m3() // m3 宽放大模式在浏览器环境中,模块的各个部分通常都是通过网络获取的,有时无法知道哪个部分会先加载。如果采用放大模式的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用”宽放大模式”。1234var module1 = ( function (mod){ //... return mod;})(window.module1 || {}); 与”放大模式”相比,"宽放大模式"就是”立即执行函数”的参数可以是空对象 输入全局变量独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。为了在模块内部调用全局变量,必须显式地将其他变量输入模块。123var module1 = (function ($) { //...})(jQuery); module1模块需要使用jQuery库,把这两个库当作参数输入module1,这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显 CommonJS和AMD 规范在CommonJS中,有一个全局性方法require(),用于加载模块。CommonJS规范主要用在服务端,例如 nodejs就遵守CommonJS规范12var math = require('math');math.add(2,3); // 5 但是,如果是客户端, math.add(2, 3)必须要等到 ’math’ 模块加载完成之后才能执行,也就是说,如果加载时间很长,整个应用就会停在那里等待。因为服务端可以同步加载模块,而浏览器主要是异步加载, 这就产生了 AMD 规范: AMD规范 它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。 AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:1require([module], callback); 第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样: 123require(['math'], function (math) { math.add(2, 3);}); 所以,AMD比较适合浏览器环境, 目前实现了 AMD 规范最流行javascript库之一是 require.js. require.js以前引入js的方法123456<script src=\"1.js\"></script><script src=\"2.js\"></script><script src=\"3.js\"></script><script src=\"4.js\"></script><script src=\"5.js\"></script><script src=\"6.js\"></script> 缺点: 浏览器会停止页面渲染,加载的文件越多,网页响应的时间越长 js文件存在依赖关系,必须严格保证加载顺序,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。 require.js加载模块 首先引入 require.js1234567891011121314151617<script src=\"res/js/require.js\"></script>require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){ // some code here});// require.config(); 配置参数require.config({ paths: { \"jquery\": \"res/js/jquery-3.1.1.min\", \"math\": \"res/js/math\" })};require(['jquery', 'math'], function ($, math){ console.log($);}); AMD模块的写法 12345678define(function (){ var add = function (x,y){ return x+y; }; return { add: add };}); 注意:require.js加载的模块,必须是按照AMD规范、用define()函数定义的模块。 加载非规范的模块但是,已经有很多模块没有严格的遵守AMD规范,像这样的模块, 其实 require 也是可以加载的,使用 require.config() 对象除了接收 paths 属性之外,还有一个 shim 属性,专门用来配置不兼容的模块。具体来说,每个模块要定义: exports值(输出的变量名),表明这个模块外部调用时的名称; deps数组,表明该模块的依赖性。 比如,jQuery的插件可以这样定义123456shim: { 'jquery.scroll': { deps: ['jquery'], exports: 'jQuery.fn.scroll' }} 二、ES6简单语法ES6 简介参考:http://es6.ruanyifeng.com/#docs/intro 主流浏览器对ES6的支持:http://kangax.github.io/compat-table/es6/ 新增语法1、let和const命令 let 声明的变量只在该变量所在模块内有效123for (let i = 0; i < 10; i++) {}console.log(i); // defined const 声明一个只读的常量。一旦声明,常量的值就不能改变。1234const PI = 3.1415;PI // 3.1415PI = 3; // Assignment to constant variable. 2、结构化赋值123456789101112var a = 1;var b = 2;var c = 3;// 等同于var [a, b, c] = [1, 2, 3];let [ , , third] = [\"foo\", \"bar\", \"baz\"];third; // bazlet [head, ...tail] = [1, 2, 3, 4];head // 1tail // [2, 3, 4] 3、块级作用域 ES5只有全局作用域和函数作用域,没有块级作用域12345678910var tmp = new Date();function f() { console.log(tmp); if (false) { var tmp = \"hello world\"; }}f(); // undefined ES6 的写法1234{ let tmp = 123;}console.log(tmp); //defined 4、模版字符串 ES5 的写法123var txt = 'hello world!';var test = document.getElementById('demo_1');test.innerHTML = '<div>'+txt+'</div>'; ES6 的写法123var txt = 'hello world!';var test = document.getElementById('demo_1');test.innerHTML = `<div>${txt}</div>`; 5、for of 循环 ES5 for in 循环12345var arr = ['a', 'b', 'c', 'd'];for (let a in arr) { console.log(a); // 0 1 2 3} ES6 for of 循环12345var arr = ['a', 'b', 'c', 'd'];for(let v of arr) { console.log(v); // a, b, c, d} 6、箭头函数1234567891011121314151617var f = v => v;//等同于var f = function(v) { return v;};var f = () => 5;// 等同于var f = function () { return 5;};var sum = (num1, num2) => num1 + num2;// 等同于var sum = function(num1, num2) { return num1 + num2;}; 7、class 基本语法 ES51234567891011function Point(x, y) { this.x = x; this.y = y;}Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')';};var p = new Point(1, 2);p.toString(); //(1, 2) ES6123456789101112class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; }}var p = new Point(1,2);p.toString(); // (1, 2) 更多教程: 阮一峰《ECMAScript 6 入门》 三、Web前端代码规范参考: 腾讯前端团队的规范文档 http://alloyteam.github.io/CodeGuide/ 四、react-native Windows + Android环境搭建react-native 官方文档: http://facebook.github.io/react-native/ 必需软件安装Node.js 安装 Node , python2https://nodejs.org/https://www.python.org/ React Native CLI1npm install -g react-native-cli Android 6.0 SDK 为了 Android SDK 的好管理和下载,直接安装 Android Studio ,并配置环境变量 Genymotion 模拟器 下载:https://www.genymotion.com/ Genymotion是基于 Oracle 的 VirtualBox 虚拟机的,所以样两个版本,一个是带有VBox 的虚拟机的,一个是不带 VBox。安装教程: http://blog.csdn.net/beiminglei/article/details/13776013 初始化项目并启动123456react-native init AwesomeProjectcd AwesomeProjectreact-native start// 等服务启动react-native run-android 测试服务地址: localhost:8081/index.android.bundle?platform=android yarn 简介: http://www.infoq.com/cn/news/2016/10/yarn 博客园开源地址: https://github.com/togayther/react-native-cnblogs 更多文章请关注微信公众号: zhiheng博客","categories":[],"tags":[{"name":"React-Native","slug":"React-Native","permalink":"https://dddreams.github.io/tags/React-Native/"},{"name":"ES6","slug":"ES6","permalink":"https://dddreams.github.io/tags/ES6/"}],"keywords":[]},{"title":"java设计模式--简单工厂模式","slug":"java设计模式之工厂模式","date":"2016-11-26T09:09:26.000Z","updated":"2017-05-19T08:19:45.863Z","comments":true,"path":"java设计模式之工厂模式.html","link":"","permalink":"https://dddreams.github.io/java设计模式之工厂模式.html","excerpt":"","text":"在《java与模式》一书中是这样描述简单工厂模式的: 简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。 应用场景就那我们熟悉的登录来说,如果应用系统有多种登录方式,例如:口令认证、域认证(口令认证通常是去数据库中验证用户,而域认证则是需要到微软的域中验证用户)。那么我们的做法就是建立一个适用与各种方式的登录接口,如下图所示: 代码示例1234// 登录验证接口public interface Login{ public boolean verify(String name, String password);} 12345678// 域认证的实现public class RegionLogin implements Login { @Override public boolean verify(String name, String password){ // do something return true; }} 12345678// 口令认证的实现public class PasswordLogin implements Login { @Override public boolean verify(String name, String password){ // do something return true; }} 我们还需要一个工厂类 LoginManager,根据不同的调用者,创建不同的登录对象并返回。如果不合法会返回一个Runtime异常。1234567891011public class LoginManager { public static Login factory(String type) { if(\"password\".equals(type)) { return new PasswordLogin(); } esle if(\"region\".equals(type)) { return new RegionLogin(); } else { throw new RuntimeException(\"没有找到登录类型\"); } }} 12345678910111213141516// 测试类public class Test { public static void main(String [] args){ String loginType = \"password\"; String name = \"name\"; String password = \"123456\"; Login login = LoginManager.factory(loginType); boolean bool = login.verify(name, password); if(bool){ // do something } else { // do something } }} 如果把上面的Test当做一个servlet的话,当客户端发起登录请求——>请求交给服务端的Servlet——>Servlet根据客户端传递的loginType调用工厂类LoginManager的factory()方法——>factory()方法根据参数loginType创建相应的登录验证类(RegionLogin或PasswordLogin)并返回——>登录验证类调用方法verify()验证用户名密码是否正确。 如果不使用简单工厂模式,假设Test为一个Servlet,变量loginType、name、password表示从客户端传递过来的参数。1234567891011121314151617181920212223242526public class Test { public static void main(String[] args){ String loginType = \"password\"; String name = \"name\"; String password = \"123456\"; // 处理口令认证 if(\"password\".equals(loginType)){ PasswordLogin passwordLogin = new PasswordLogin(); boolean bool = passwordLogin.verify(name, password); if(bool){ // do something } else { // do something } } else if(\"region\".equals(loginType)){ RegionLogin reginLogin = new RegionLogin(); boolean bool = reginLogin.verify(name, password); if(bool){ // do something } else { // do something } } }} 这样的代码会不会让人蛋疼,,,,, 《JAVA与模式》一书中使用java.text.DataFormat类作为简单工厂模式的典型例子叙述。 简单工厂模式的优点模式的核心是工厂类。这个类含有必要的逻辑判断,可以决定在什么时候创建哪一个登录验证类的实例,而调用者则可以免除直接创建对象的责任。简单工厂模式通过这种做法实现了对责任的分割,当系统引入新的登录方式的时候无需修改调用者。 简单工厂模式的缺点这个工厂类集中了所以的创建逻辑,当有复杂的多层次等级结构时,所有的业务逻辑都在这个工厂类中实现。什么时候它不能工作了,整个系统都会受到影响。 更多文章请关注微信公众号: zhiheng博客","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"https://dddreams.github.io/tags/Java/"},{"name":"设计模式","slug":"设计模式","permalink":"https://dddreams.github.io/tags/设计模式/"}],"keywords":[]},{"title":"css伪类&伪元素","slug":"css伪类-伪元素","date":"2016-11-25T06:36:34.000Z","updated":"2017-05-19T08:20:21.094Z","comments":true,"path":"css伪类-伪元素.html","link":"","permalink":"https://dddreams.github.io/css伪类-伪元素.html","excerpt":"","text":"样式规则通常根据元素在文档结构中的位置应用到元素;但是,级联样式表使用伪类和伪元素的概念,根据位于文档树之外的信息来允许格式设置。伪元素用于处理元素的子部分(例如 :first-letter 或 :first-line),而伪类根据元素名、元素属性或元素内容以外的特征(例如 :first-child、:visited 或 :hover)将元素分类。伪类通常是动态的,这意味着在用户与文档交互的同时元素可以获取或丢失伪类。 伪类当我们指定某一元素被选中后的特殊状态时,一个 CSS 伪类 会被作为一个关键词添加到选择器上. 例如 :hover 会在当用户鼠标指针悬停在由选择器指定的元素上时应用一个样式. 伪类连同伪元素一起, 他们允许你不仅仅是根据文档DOM树中的内容对元素应用样式,而且还允许你根据诸如像导航历史这样的外部因素来应用样式(:visited, 为例), 同样的,可以根据内容的状态 (例如 在一些表单元素上的 :checked ), 或者鼠标的位置 (例如 :hover 让你知道是否鼠标在一个元素上悬浮)来应用样式. :link :visited :active :hover 链接的不同状态都可以不同的方式显示,这些状态包括:活动状态,已被访问状态,未被访问状态,和鼠标悬停状态。1234a:link {color: #FF0000} /* 未访问的链接 */a:visited {color: #00FF00} /* 已访问的链接 */a:hover {color: #FF00FF} /* 鼠标移动到链接上 */a:active {color: #0000FF} /* 选定的链接 */ 其中 :hover 不仅限于链接之上,其他元素皆可使用。 :focus focus 在一个元素成为焦点时生效,用户可以通过键盘或鼠标激活焦点(例如:一次表单输入)。12345.first-name:focus { color: red; }.last-name:focus { color: lime; }<input class=\"first-name\" value=\"input value\"><input class=\"last-name\" value=\"input value\"> :first-child :last-child :first-of-type :last-of-type :first-child代表了某个元素,这个元素是它父元素的的第一个子元素.:first-of-type伪类表示在父容器内其所有种类元素的第一个元素。:last-child伪类表示其父容器内最后一个子元素。:last-of-type伪类表示父容器内其所有种类元素的最后一个元素。 1234567891011121314151617span:first-child { background-color: lime;}ul :last-of-type { color: orange;}<div> <span>This span is limed!</span> <span>This span is not. :(</span></div><ul> <li>Lorem ipsum dolor sit amet. <span>Lorem ipsum dolor sit amet.</span> <span>This text will be orange.</span></li> <li>Lorem ipsum dolor sit amet.</li> <li>This text will be orange.</li></ul> 效果:This span is limed! This span is not. :( 最后一个li和最后一个span元素的文本颜色将为橙色。 :not :not伪类也被称为反伪类。基本上它接收一个参数,在括号内书写另外一个“选择器”。实际上参数可以是另外一个选择器。 这里可能是链接式的,但是不会包含:not选择器。 下面示例中。:not伪类匹配参数外的元素。12345678910li:not(.first-item) { color: orange;}<ul> <li class=\"first-item\">Lorem ipsum dolor sit amet.</li> <li>Lorem ipsum dolor sit amet.</li> <li>Lorem ipsum dolor sit amet.</li> <li>Lorem ipsum dolor sit amet.</li></ul>","categories":[],"tags":[{"name":"Css","slug":"Css","permalink":"https://dddreams.github.io/tags/Css/"}],"keywords":[]},{"title":"css实现计数器","slug":"css实现计数器","date":"2016-11-25T02:55:15.000Z","updated":"2017-05-19T08:20:24.271Z","comments":true,"path":"css实现计数器.html","link":"","permalink":"https://dddreams.github.io/css实现计数器.html","excerpt":"","text":"无序列表无序列表的每个列表项都用同样的方式标记。CSS 有三种标记样式: disc circle square示例:12345678910li.open {list-style: circle;}li.closed {list-style: disc;}<ul> <li class=\"open\">Lorem ipsum</li> <li class=\"closed\">Dolor sit</li> <li class=\"closed\">Amet consectetuer</li> <li class=\"open\">Magna aliquam</li> <li class=\"closed\">Autem veleum</li></ul> 结果: Lorem ipsum Dolor sit Amet consectetuer Magna aliquam Autem veleum 有序列表在有序列表中,每个列表项都被标记了不同的序号。用list-style属性指定标记样式: decimal lower-roman upper-roman lower-latin upper-latin 示例:123456789ol.info {list-style: upper-latin;}<ol class=\"info\"> <li>ssss</li> <li>ssss</li> <li>ssss</li> <li>ssss</li> <li>ssss</li></ol> 结果: ssssssssssssssssssss 计数器你可以用计数器来计数任何元素,不仅是列表元素。比如,在某些文档中你可能想计数标题和段落。 要想计数,你必须定义一个计数器。 在计数开始前的某个元素上,设置 counter-reset属性以重置计数器。被计数元素的父节点是一个不错的选择。当然,任何出现在被计数元素前面的元素都可以。 设置每个需要计数的元素的counter-increment 属性为你的计数器名。 通过为选择器增加 :before 或 :after 并设置 content 属性来显示计数器。 (如上一节所示, 内容). 在content属性的值中设置 counter(),在括号内填上计数器的名字。可选的是设置计数器类型。其类型和前面一节 有序列表 中相同。 正常情况下,显示计数器的元素也会递增计数器。 示例:1234567891011121314h3.numbered {counter-reset: mynum;}p.numbered:before { content: counter(mynum) \": \"; counter-increment: mynum; font-weight: bold;}<h3 class=\"numbered\">Numbered paragraphs</h3><p class=\"numbered\">Lorem ipsum</p><p class=\"numbered\">Dolor sit</p><p class=\"numbered\">Amet consectetuer</p><p class=\"numbered\">Magna aliquam</p><p class=\"numbered\">Autem veleum</p> 结果:Numbered paragraphs A: Lorem ipsum B: Dolor sit C: Amet consectetuer D: Magna aliquam E: Autem veleum 更多文章请关注微信公众号: zhiheng博客","categories":[],"tags":[{"name":"Css","slug":"Css","permalink":"https://dddreams.github.io/tags/Css/"}],"keywords":[]},{"title":"代码规范之Web","slug":"代码规范之Web","date":"2016-11-23T12:26:46.000Z","updated":"2017-05-19T08:21:08.571Z","comments":true,"path":"代码规范之Web.html","link":"","permalink":"https://dddreams.github.io/代码规范之Web.html","excerpt":"","text":"命名规则上一篇总结了 java 代码规范,规则都来源于 google 的代码规范,这一篇总结 web 前端的规范。 项目命名:全部使用小写字母,用下划线隔开。例:my_project_name目录命名:采用项目命名规则,有复数结构时,要采用复数命名法。例:styles,images,scripts文件名:都采用小写字母,用下划线隔开加后缀。例:index_example.html,style_base.css,example_model.js。 HTML语法: 缩进使用soft tab(4个空格); 嵌套的节点应该缩进; 在属性上,使用双引号,不要使用单引号; 属性名全小写,用中划线做分隔符; 不要在自动闭合标签结尾处使用斜线(HTML5 规范 指出他们是可选的); 不要忽略可选的关闭标签,例:</li> 和 </body>。 虽然doctype不区分大小写,但是按照惯例,doctype大写关于html属性,大写还是小写 应在html标签上加上lang属性。这会给语音工具和翻译工具帮助,告诉它们应当怎么去发音和翻译。更多关于 lang 属性的说明在这里;而微软给出了一份更加详细的语言列表.aspx),其中细分了zh-cn, zh-hk, zh-tw; 指定字符编码通常为’UTF-8’; IE兼容模式: 用 标签可以指定页面应该用什么版本的IE来渲染; 1234567891011121314151617181920212223242526<!DOCTYPE html><html lang=\"en-us\"> <head> <meta charset=\"UTF-8\"> <meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge\"> <title>Page title</title> <!-- External CSS --> <link rel=\"stylesheet\" href=\"code_guide.css\"> <!-- In-document CSS --> <style> ... </style> </head> <body> <img src=\"images/company_logo.png\" alt=\"Company\"> <h1 class=\"hello-world\">Hello, world!</h1> <!-- External JS --> <script src=\"code_guide.js\"></script> <!-- In-document JS --> <script> ... </script> </body></html> 属性顺序属性应该按照特定的顺序出现以保证易读性; class id name data-* src, for, type, href, value , max-length, max, min, pattern placeholder, title, alt aria-*, role required, readonly, disabled class是为高可复用组件设计的,所以应处在第一位;id更加具体且应该尽量少使用,所以将它放在第二位。 JS生成标签 在JS文件中生成标签让内容变得更难查找,更难编辑,性能更差。应该尽量避免这种情况的出现。 减少标签数量 在编写HTML代码时,需要尽量避免多余的父节点;很多时候,需要通过迭代和重构来使HTML变得更少。1234567<!-- Not well --><span class=\"avatar\"> <img src=\"...\"></span><!-- Better --><img class=\"avatar\" src=\"...\"> CSS缩进 使用soft tab(4个空格)。123456789.element { position: absolute; top: 10px; left: 10px; border-radius: 10px; width: 50px; height: 50px;} 命名 类名使用小写字母,以中划线分隔id采用驼峰式命名scss中的变量、函数、混合、placeholder采用驼峰式命名 属性简写 属性简写需要你非常清楚属性值的正确顺序,而且在大多数情况下并不需要设置属性简写中包含的所有值,所以建议尽量分开声明会更加清晰;margin 和 padding 相反,需要使用简写;常见的属性简写包括: font background transition animation 123456789101112/* not good */.element { transition: opacity 1s linear 2s;}/* good */.element { transition-delay: 2s; transition-timing-function: linear; transition-duration: 1s; transition-property: opacity;} 颜色 颜色16进制用小写字母;颜色16进制尽量用简写。1234567891011/* not good */.element { color: #ABCDEF; background-color: #001122;}/* good */.element { color: #abcdef; background-color: #012;} 杂项 不允许有空的规则; 元素选择器用小写字母; 去掉小数点前面的0; 去掉数字中不必要的小数点和末尾的0; 属性值’0’后面不要加单位; 同个属性不同前缀的写法需要在垂直方向保持对齐,具体参照右边的写法; 无前缀的标准属性应该写在有前缀的属性后面; 不要在同个规则里出现重复的属性,如果重复的属性是连续的则没关系; 不要在一个文件里出现两个相同的规则; 用 border: 0; 代替 border: none;; 选择器不要超过4层(在scss中如果超过4层应该考虑用嵌套的方式来写); 发布的代码中不要有 @import; 尽量少用’*’选择器。 JavaScript缩进 使用soft tab(4个空格)。12345678var x = 1, y = 1;if (x < y) { x += 10;} else { x += 1;} 分号 以下几种情况后需加分号: 变量声明 表达式 return throw break continue do-while 12345678910/* var declaration */var x = 1;/* expression statement */x++;/* do-while */do { x++;} while (x < 10); 引号 最外层统一使用单引号。123456// not goodvar x = \"test\";// goodvar y = 'foo', z = '<div id=\"test\"></div>'; 变量命名 标准变量采用驼峰式命名(除了对象的属性外,主要是考虑到cgi返回的数据) ‘ID’在变量名中全大写 ‘URL’在变量名中全大写 ‘Android’在变量名中大写第一个字母 ‘iOS’在变量名中小写第一个,大写后两个字母 常量全大写,用下划线连接 构造函数,大写第一个字母 jquery对象必须以’$’开头命名 1234567891011121314var thisIsMyName;var goodID;var reportURL;var AndroidVersion;var iOSVersion;var MAX_COUNT = 10;function Person(name) { this.name = name;}// not goodvar body = $('body');// goodvar $body = $('body'); 变量声明 一个函数作用域中所有的变量声明尽量提到函数首部,用一个var声明,不允许出现两个连续的var声明。1234567891011function doSomethingWithItems(items) { // use one var var value = 10, result = value + 10, i, len; for (i = 0, len = items.length; i < len; i++) { result += 10; }} 函数 无论是函数声明还是函数表达式,’(‘前不要空格,但’{‘前一定要有空格; 函数调用括号前不需要空格; 立即执行函数外必须包一层括号; 不要给inline function命名; 参数之间用’, ‘分隔,注意逗号后有一个空格。 数组、对象 对象属性名不需要加引号; 对象以缩进的形式书写,不要写在一行; 数组、对象最后不要有逗号。1234567891011121314151617// not goodvar a = { 'b': 1};var a = {b: 1};var a = { b: 1, c: 2,};// goodvar a = { b: 1, c: 2}; 文档注释 各类标签@param, @method等请参考usejsdoc和JSDoc Guide; 建议在以下情况下使用: 所有常量所有函数所有类1234567891011121314151617/** * @func * @desc 一个带参数的函数 * @param {string} a - 参数a * @param {number} b=1 - 参数b默认值为1 * @param {string} c=1 - 参数c有两种支持的取值</br>1—表示x</br>2—表示xx * @param {object} d - 参数d为一个对象 * @param {string} d.e - 参数d的e属性 * @param {string} d.f - 参数d的f属性 * @param {object[]} g - 参数g为一个对象数组 * @param {string} g.h - 参数g数组中一项的h属性 * @param {string} g.i - 参数g数组中一项的i属性 * @param {string} [j] - 参数j是一个可选参数 */function foo(a, b, c, d, g, j) { ...} null 适用场景: 初始化一个将来可能被赋值为对象的变量 与已经初始化的变量做比较 作为一个参数为对象的函数的调用传参 作为一个返回对象的函数的返回值不适用场景: 不要用null来判断函数调用时有无传参 不要与未初始化的变量做比较 1234567891011121314151617181920// not goodfunction test(a, b) { if (b === null) { // not mean b is not supply ... }}var a;if (a === null) { ...}// goodvar a = null;if (a === null) { ...} undefined 永远不要直接使用undefined进行变量判断; 使用typeof和字符串’undefined’对变量进行判断。123456789// not goodif (person === undefined) { ...}// goodif (typeof person === 'undefined') { ...} 杂项 不要混用tab和space; 不要在一处使用多个tab或space; 换行符统一用’LF’; 对上下文this的引用只能使用’_this’, ‘that’, ‘self’其中一个来命名; 行尾不要有空白字符; switch的falling through和no default的情况一定要有注释特别说明; 不允许有空的代码块。123456789101112131415161718192021222324252627282930313233343536// not goodvar a = 1;function Person() { // not good var me = this; // good var _this = this; // good var that = this; // good var self = this;}// goodswitch (condition) { case 1: case 2: ... break; case 3: ... // why fall through case 4 ... break; // why no default}// not good with empty blockif (condition) {} 更多文章请关注微信公众号: zhiheng博客","categories":[],"tags":[{"name":"Web","slug":"Web","permalink":"https://dddreams.github.io/tags/Web/"}],"keywords":[]},{"title":"代码规范之 java ","slug":"代码规范之-java","date":"2016-10-29T03:05:40.000Z","updated":"2017-05-19T08:21:13.101Z","comments":true,"path":"代码规范之-java.html","link":"","permalink":"https://dddreams.github.io/代码规范之-java.html","excerpt":"","text":"对于代码规范,我是一个有洁癖的人,尤其是在 html 页面中更加注重。公司有几个哥们,写代码完全不按常理出牌,最简单的代码缩进都懒得去做,更别说美观了,每次修改bug都让人很头疼,读他们的代码就像是在吃黄连。尤其是我这种有代码洁癖的人,第一眼看上去,必然会在心里骂爹骂娘的(我去年买了个表)。今天这篇文章我们就来说说大神们的代码规范。 命名包名: 包名一般都是用公司的域名反着来写的,最后是业务名称,通用小写字母,连续的单词只是简单地连接起来,不使用下划线,例如: com.google.XXX.action 。文件名:源文件以其最顶层的类名来命名,大小写敏感,文件扩展名为 .java 名称与业务相关。源文件编码格式为 utf-8 .类名: 类的名字必须由大写字母开头而单词中的其他字母均为小写;如果类名称由多个单词组成,则每个单词的首字母均应为大写例如TestPage;如果类名 称中包含单词缩写,则这个所写词的每个字母均应大写,如:XMLExample,还有一点命名技巧就是由于类是设计用来代表对象的,所以在命名类时应尽量选择名词。例如: User方法名:方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头,使用驼峰命名法。例如: sendMessge()常量名:常量的名字应该都使用大写字母,并且指出该常量完整含义。如果一个常量名称由多个单词组成,则应该用下划线来分割这些单词。 例如:1234567891011121314// goodstatic final int NUMBER = 5;static final ImmutableList<String> NAMES = ImmutableList.of(\"Ed\", \"Ann\");static final Joiner COMMA_JOINER = Joiner.on(','); // because Joiner is immutablestatic final SomeMutableType[] EMPTY_ARRAY = {};enum SomeEnum { ENUM_CONSTANT }// badstatic String nonFinal = \"non-final\";final String nonStatic = \"non-static\";static final Set<String> mutableCollection = new HashSet<String>();static final ImmutableSet<SomeMutableType> mutableElements = ImmutableSet.of(mutable);static final Logger logger = Logger.getLogger(MyClass.getName());static final String[] nonEmptyArray = {\"these\", \"can\", \"change\"}; 属性名:参数的命名规范和方法的命名规范相同,而且为了避免阅读程序时造成迷惑,请在尽量保证参数名称为一个单词的情况下使参数的命名尽可能明确。驼峰式命名法(CamelCase):驼峰式命名法分大驼峰式命名法(UpperCamelCase)和小驼峰式命名法(lowerCamelCase)。 有时,我们有不只一种合理的方式将一个英语词组转换成驼峰形式,如缩略语或不寻常的结构(例如”IPv6”或”iOS”)。Google指定了以下的转换方案。 名字从散文形式(prose form)开始: 把短语转换为纯ASCII码,并且移除任何单引号。例如:”Müller’s algorithm”将变成”Muellers algorithm”。 把这个结果切分成单词,在空格或其它标点符号(通常是连字符)处分割开。推荐:如果某个单词已经有了常用的驼峰表示形式,按它的组成将它分割开(如”AdWords”将分割成”ad words”)。 需要注意的是”iOS”并不是一个真正的驼峰表示形式,因此该推荐对它并不适用。 现在将所有字母都小写(包括缩写),然后将单词的第一个字母大写: 每个单词的第一个字母都大写,来得到大驼峰式命名。 除了第一个单词,每个单词的第一个字母都大写,来得到小驼峰式命名。 最后将所有的单词连接起来得到一个标识符。例如:12345678Prose form Correct Incorrect------------------------------------------------------------------\"XML HTTP request\" XmlHttpRequest XMLHTTPRequest\"new customer ID\" newCustomerId newCustomerID\"inner stopwatch\" innerStopwatch innerStopWatch\"supports IPv6 on iOS?\" supportsIpv6OnIos supportsIPv6OnIOS\"YouTube importer\" YouTubeImporter YoutubeImporter* javadoc注释: Java除了可以采用我们常见的注释方式之外,Java语言规范还定义了一种特殊的注释,也就是我们所说的Javadoc注释,它是用来记录我们代 码中的API的。Javadoc注释是一种多行注释,以结束,注释可以包含一些HTML标记符和专门的关键词。使用Javadoc 注释的好处是编写的注释可以被自动转为在线文档,省去了单独编写程序文档的麻烦。例如:12345/** * Multiple lines of Javadoc text are written here, * wrapped normally... */public int method(String p1) { ... } 至少在每个public类及它的每个public和protected成员处使用Javadoc, 编码规范package语句: package 语句不换行,(即package语句写在一行里)import语句: 一般不使用通配符,即:import java.util.*;import语句不换行(每个import语句独立成行)。缩进: 缩进一个 tab大括号: 大括号与if, else, for, do, while语句一起使用,即使只有一条语句(或是空),也应该把大括号写上。12345if(value){ ...} else { ...} 列限制: 80或100,一个项目可以选择一行80个字符或100个字符的列限制,除了下述例外,任何一行如果超过这个字符数限制,必须自动换行。自动换行时,第一行后的每一行至少比第一行多缩进4个空格。数组: 以下写法都是OK的123456789101112131415new int[] { 0, 1, 2, 3}new int[] { 0, 1, 2, 3}new int[] { 0, 1, 2, 3}new int[] {0, 1, 2, 3} switch语句:在一个switch块内,每个语句组要么通过break, continue, return或抛出异常来终止,要么通过一条注释来说明程序将继续执行到下一个语句组, 任何能表达这个意思的注释都是OK的(典型的是用// fall through)。这个特殊的注释并不需要在最后一个语句组(一般是default)中出现。示例:1234567891011switch (input) { case 1: case 2: prepareOneOrTwo(); // fall through case 3: handleOneTwoOrThree(); break; default: handleLargeNumber(input);} 注解(Annotations):注解紧跟在文档块后面,应用于类、方法和构造函数,一个注解独占一行。这些换行不属于自动换行(第4.5节,自动换行),因此缩进级别不变。例如:123@Override@Nullablepublic String getNameIfPresent() { ... } 注释:块注释与其周围的代码在同一缩进级别。它们可以是/* ... */风格,也可以是// ...风格。对于多行的/* ... */注释,后续行必须从*开始, 并且与前一行的*对齐。以下示例注释都是OK的。1234/* * This is // And so /* Or you can * okay. // is this. * even do this. */ */ 捕获的异常:不能忽视除了下面的例子,对捕获的异常不做响应是极少正确的。(典型的响应方式是打印日志,或者如果它被认为是不可能的,则把它当作一个AssertionError重新抛出。) 如果它确实是不需要在catch块中做任何响应,需要做注释加以说明(如下面的例子)。12345678try { int i = Integer.parseInt(response); return handleNumericResponse(i);} catch (NumberFormatException ok) { // it's not numeric; that's fine, just continue}return handleTextResponse(response); 例外:在测试中,如果一个捕获的异常被命名为expected,则它可以被不加注释地忽略。下面是一种非常常见的情形,用以确保所测试的方法会抛出一个期望中的异常, 因此在这里就没有必要加注释。12345try { emptyStack.pop(); fail();} catch (NoSuchElementException expected) {} 更多文章请关注微信公众号: zhiheng博客","categories":[],"tags":[{"name":"Java","slug":"Java","permalink":"https://dddreams.github.io/tags/Java/"}],"keywords":[]},{"title":"chrome插件,吐血推荐","slug":"chrome插件,吐血推荐","date":"2016-10-24T01:22:21.000Z","updated":"2017-05-19T08:20:30.764Z","comments":true,"path":"chrome插件,吐血推荐.html","link":"","permalink":"https://dddreams.github.io/chrome插件,吐血推荐.html","excerpt":"","text":"今天是1024程序员节,祝各位大神节日快乐,福利就不发了,请自行google ,好了,言归正传。当说起 chrome 插件时, 谷歌百度一搜,众说纷纭说有谁的喜好,今天我也推荐一下我使用的 chrome 插件, 注意这里是 吐血推荐。 Adblock Plus该插件的介绍是这样说的:12超过5000万人使用,免费的广告拦截器,可阻止所有烦人的广告及恶意软件和跟踪。享受没有恼人广告的网络世界。 正如它的介绍,你还在为浏览网页时突然弹出诱人的图片而尴尬吗?你还在为一些烦人的广告而烦恼吗?那么你就 out 了,来吧,在你的 chrome 上安装 Adblock Plus 吧。 chrome 应用商店中有很多拦截广告的插件,但是好多都是收费的,对于一个穷屌丝来说,免费万岁。 Save to Pocket对于喜欢淘金的童鞋,这个插件特别有用,当我们看到一篇技术文章又特别感兴趣的时候,轻轻点击 chrome 右上角 Save to Pocket 的图标,就会将这篇文章保存在你的帐号中,(亮点在这里)并且会自动同步到你的其他设备,当然前提是你要安装 pocket 客户端,对于现在移动端碎片化阅读时代,你可以随时看到你收藏 的文章,而不是被朋友圈的微商广告而绑架。 LiveReload前端的同学注意啦,当我们在切页面时,还在为 IDE 和 chrome 之间来回切换,并且每次拿起鼠标点击刷新按钮或者狂按F5而烦恼吗,LiveReload 可以帮你节省这点时间,安装这个插件,当你在你的 IDE 中按下 ctrl+s 时,你的浏览器会自动刷新,方便你看效果,提高开发效率,这样是不是高逼格了不少啊。 当然这需要对你的 IDE 做点手脚,对 sublime-text 而言,需要安装 LiveReload 插件,使用是开启即可。对于 atom 而言,它已经集成了LiveReload 这个插件,只需开启就 OK 了。对于 vs code 而言,微软商店中没有这个插件,不知道怎么使用,又知道的童鞋可以留言或者直接联系我 Email: [email protected] , 微信: Dd_ (ding289750872)。 JavaScript Errors Notifier这个插件在开发时也很有用,尤其在调试 js 时,我们不用打开 chrome devtools 来查看 js 报什么错误,它会在浏览器右下方弹出错误信息 Momentum这个是美化 chrome 新选项卡的插件,先看看我今天美图是不是比以前的大白页好看多了,有的人也喜欢用掘金 这个插件,它也是优化了 chrome 新选项卡,不过它推荐了 github 和很多社区上一些优质的项目及技术文章,也是不错的一个插件,贴个截图 有道词典Chrome划词插件哈哈,对于我这种英语屌丝来说还是装一个翻译的插件吧,这有助于浏览英文网站,和众多翻译软件类似,有划词翻译,指词翻译(我一般是关闭指词翻译的)的功能,总之是很好用的。 更多文章请关注微信公众号: zhiheng博客","categories":[],"tags":[{"name":"工具","slug":"工具","permalink":"https://dddreams.github.io/tags/工具/"},{"name":"Chrome","slug":"Chrome","permalink":"https://dddreams.github.io/tags/Chrome/"}],"keywords":[]},{"title":"坚持久了,就成为习惯了","slug":"坚持久了,就成为习惯了","date":"2016-10-12T09:38:17.000Z","updated":"2017-05-19T08:18:53.956Z","comments":true,"path":"坚持久了,就成为习惯了.html","link":"","permalink":"https://dddreams.github.io/坚持久了,就成为习惯了.html","excerpt":"","text":"  这篇文章的思绪来源于一个同样是从事IT行业的大牛人物,同样在用微信公众号推送技术文章,同样喜于分享,勤于积累,感兴趣的童鞋可以关注它的微信公众号:AndroidDeveloper,id:googdev(这里不是广告哦)。昨天看到他的一篇文章每天能写文章(博客、微信公众号)的人是怎么安排时间的? 和自己的初衷是一样一样的,于是就有了这篇文章,也是为自己加油大气。   在学校的时候各大网站都推出了自己的博客,我也在新浪,网易,CSDN,博客园等开通了自己的博客,但那都是因为好玩,偶尔上去随便看看,没有认真的写过博文,也没有好好维护过。自从上班以来,接触的新技术,新知识越来越多,而且在网上也出现了越来越多的垃圾软文,对于一个新手来说,会走很多的弯路,大部分原因来源于这些垃圾软文,因此我慢慢养成了习惯,上网查东西,都是去官网或者是比较正规的技术社区,而且将惯用的搜索引擎从百度换成了必应和google,也决定将写博客作为自己的一种习惯。    刚开始是在CSDN上写,现在利用 hexo 和 github 搭建了自己的博客zhiheng`blogs 最近又开了微信公众号:zhiheng博客,id: ddAnswer。折腾了这么多天,现在万事俱备,只欠东风了,那就是如何坚持写下去,这的却是个很困难的事,而且要写出有质量的文章那就更不容易了。最近发表的几篇文章都是通过一些技术社区和书籍上摘来的,以前看着别人每天更新博客觉得挺简单的,现在自己写起来才真的感觉到没什么可以写的,不过还是要坚持写下去,让这一种坚持成为一中习惯。加油,,,Dd_ ~_~ 更多文章请关注微信公众号: zhiheng博客","categories":[],"tags":[{"name":"杂谈","slug":"杂谈","permalink":"https://dddreams.github.io/tags/杂谈/"}],"keywords":[]},{"title":"html css 垂直居中(二)","slug":"html-css-垂直居中(二)","date":"2016-10-10T07:31:43.000Z","updated":"2017-05-19T08:20:04.969Z","comments":true,"path":"html-css-垂直居中(二).html","link":"","permalink":"https://dddreams.github.io/html-css-垂直居中(二).html","excerpt":"","text":"在html css 垂直居中(一) 中介绍了《css 揭秘》 中的垂直居中的方法,很显然在很多情况下是可以解决的,也是最好的解决方案,但是在某些特定的情况下还是要根据具体的情况而选择合适的方法,这篇同样介绍了一些垂直居中的方法,分享于大家兵记录。 line-heightline-heigth 属性是针对:父元素高度确定的单行文本(内联元素)12345678910//html<div class=\"parent\"> <div class=\"child\"></div></div>//css<style> .child{ line-height: 100px; }</style> 这种方式虽然简单但缺点很明显,仅限于单行文本,而且文字超出容器不会自适应。但它只能用于 inline 元素 table-cell父元素高度确定的多行文本(内联元素)1234567891011.parent{ border: 1px solid blue; height: 200px; /* maybe any height */ display: table;}.child{ display: table-cell; border: 1px solid green; vertical-align: middle;} 设置 display: table-cell; 也可以实现垂直居中,但是也存在很多不足,文字超出容器不会自适应。但它只能用于 inline 元素 伪元素为一个元素添加为元素,相当于为当前元素添加了子元素,因此为了生成一个 100% 高度的伪元素,我们需要对父元素添加伪元素1234567891011121314151617181920.parent{ border: 1px solid blue; height: 200px;}.parent::before { content: ''; display: inline-block; vertical-align: middle; height: 100%; font-size: 0;}.child{ display: inline-block; vertical-align: middle; border: 1px solid green; width: 100%; box-sizing: border-box;} 使用伪元素的缺点在于,当我们要使用父元素的伪元素做一些操作时,同时又让其垂直居中,那我们就无能为力了。 transform使用 transform 可以用 translateY(-50%) 来达到 - height/2 的目的,而不需要知道居中元素的高度。1234567891011121314151617181920212223//html<div class=\"container\"> <div class=\"vertical\"> <p> transform 实现垂直居中 </p> </div></div>//css<style> .container { border: 1px solid blue; height: 200px; /* maybe any height */ } .vertical { position: relative; top: 50%; transform: translateY(-50%); border: 1px solid green; }</style> flexbox最终我们还是要用flexbox,在前面一篇文章中也提到了flexbox,在此提到加深记忆。123456789101112131415161718192021//html<div class=\"container\"> <p id=\"p1\">flexbox 垂直居中111 </p> <p id=\"p2\">flexbox 垂直居中222</p></div>//css<style>.container { display: flex; flex-direction: column; justify-content: center; height: 300px; border: 1px solid blue;}p { border: 1px solid green;}</style> 需要注意的是 CSS3 的支持问题。例如 IE 需要 IE11 才能支持。","categories":[],"tags":[{"name":"Css","slug":"Css","permalink":"https://dddreams.github.io/tags/Css/"},{"name":"Html","slug":"Html","permalink":"https://dddreams.github.io/tags/Html/"}],"keywords":[]},{"title":"工作一年之后","slug":"工作一年之后","date":"2016-10-09T12:33:42.000Z","updated":"2017-05-19T08:19:05.410Z","comments":true,"path":"工作一年之后.html","link":"","permalink":"https://dddreams.github.io/工作一年之后.html","excerpt":"","text":"随着国庆节的结束,我来万维也整一年了,从此真正的踏上了程序员的路,这一年来,总的来说,除了加班还是加班,苦逼的不要不要的。 踏入前端来公司不久,就参加了公司的前端课题组,对于前端之前的认识只是 html + css + js ,2015年是前端发展的重要的一年,随着移动互联网的发展,用户体验变得比以往更重要,响应式页面设计(RWD )也成为网站的必备特性,RWD 能让最终用户在不同尺寸的设备上访问网站,包括手机和平板浏览器。还有充满创意的布局方式,大图片的背景,文字各式各样的排版等都推进着前端的发展。对于设计响应式的页面,研究过一些,由于在实际工作中不怎么用到,也只是停留在入门阶段。下面是这一年来接触到的新技术和对前端的更深层次的认识。 Nodejs不得不说的 nodejs ,敢说 nodejs 是这一年来最火的技术没有之一,在来公司之前对于 nodejs 只是概念性的。由于它的火爆,我也尝试性的写了 demo ,虽然不是很完善,但也是麻雀虽小,五脏俱全。基于 nodejs 延伸的框架 express 和 koa 都尝试了一下,也仅仅是入门,往后如果要用到还需要大量学习。 框架这一年火起来的很多前端框架,facebook 的 react,google 的 angularjs,也是火的一塌糊涂,我荣幸的体验了 react ,它的却是一个非常棒的框架,不愧是 facebook 的杰作, 还有react native 移动端的框架,一直想体验的,但是由于部分原因没有进行尝试,今后肯定会尝试他的。 ES6es6 的发布也是在今年,它增加了很多简单实用的特性,在众多编程语言中相信 js 会占领一大部分的天下。由于部分浏览器还不能对 es6 完全支持,公司内部也只是一小部分的人使用,当然我是其中之一。对于 es6 也需要进一步探索和使用。 自动化构建工具前端自动化构建工具 gulp 和 webpack 这里给出的是 github 上的地址,webpack 的官网实看着实在有点头疼。我自己也搭建了 gulp 和 webpack 的环境,不是很完善,仅仅添加了在工作中用到的 es6 基于babel 转换为 es5 和 less 转 css 的插件。地址: learn-gulp learn-webpak . 结语总之,这一年收货不多,各种新技术层次不群,每种都在尝试,但是没有用到实际的项目中, 这也需要在今后更加努力学习新技术,并用到实际项目中来,继续加油~~ Dd","categories":[],"tags":[{"name":"总结","slug":"总结","permalink":"https://dddreams.github.io/tags/总结/"}],"keywords":[]},{"title":"html css 垂直居中(一)","slug":"html-css-垂直居中","date":"2016-09-26T10:25:56.000Z","updated":"2017-05-19T08:20:14.950Z","comments":true,"path":"html-css-垂直居中.html","link":"","permalink":"https://dddreams.github.io/html-css-垂直居中.html","excerpt":"","text":"下面是《css揭秘》一书中提出的方案,站在巨人的肩膀上,记录并分享给大家 对于水平居中的,如果是内联元素,在它父级元素上设置text-align:center;,如果是块级元素,可以使用margin:auto;来让其水平居中。然而垂直居中就没那么简单了,由于场景不同,它的解决方案也多种多样。 绝对定位的方式123456789main{ position: absolute; top: 50%; left: 50%; margin-top: -3em; /* 6/2 = 3 */ margin-left: -9em; /* 18/2 = 9 */ width: 18em; height: 6em;} 这样有一个弊端,就是要有固定的宽高;还有两种方式与上面的实现是相同的。1234567main{ position: absolute; top: calc(50% - 3em); left: calc(50% - 9em); width: 18em; height: 6em;} 123456main{ position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);} 视窗单位方案CSS Values and Units Level 3定义了一种新的单位,称为相对视窗(viewport-relative)长度单位。 vw是相对于视窗的宽度。与你预期刚好相反,1vw相当于视窗宽度的1%,而不是100% 与vw相似的是,1vh相当于视窗高度的1% 如果视窗的宽度小于高度,1vmin等于1vw,反之,如果视窗宽度大于高度,1vmin等于1vh 如果视窗的宽度大于高度,1vmax等于1vw,反之,如果视窗宽度小于高度,1vmax等于1vh 123456main{ width: 18em; padding: 1em 1.5em; margin: 50vh auto 0; transform: translateY(-50%);} Flexbox的解决方案这无疑是最好的解决方案,因Flexbox的出现就是为了解决这样的问题。其他解决方案仍然可用,唯一原因是他们能更好的在浏览器上呈现,不过Flexbox在现代浏览器也得到更好的好支持。12345678body { display: flex; min-height: 100vh; margin: 0;}main { margin: auto;} Flexbox的另一个优点是,可以让匿名容器垂直居中。1<main>Center me, please!</main> 我们可以在和需要居中的元素使用相同的属性,同时使用margin:auto做为备用,以于优雅降级。1234567main { display: flex; align-items: center; justify-content: center; width: 18em; height: 10em;} 新特性:对齐所有东西CSS Box Alignment Level 3已经在计划,在未来我们甚至不需要使用不同的布局模式就能非常容易的实现垂直居中,我们只需要像下面这样做:1align-self: center; 不管元素上使用其他样式,这个将来都能运行。这听起来令人难以置信,但将来在浏览器中是可以渲染的。","categories":[],"tags":[{"name":"Css","slug":"Css","permalink":"https://dddreams.github.io/tags/Css/"}],"keywords":[]},{"title":"Test","slug":"Test","date":"2016-09-21T09:31:04.000Z","updated":"2017-05-19T08:22:26.174Z","comments":true,"path":"Test.html","link":"","permalink":"https://dddreams.github.io/Test.html","excerpt":"","text":"首次搭建,也花了不少时间,这是一个测试的文章, 测试代码123456var test = 'test';console.log(test); // testfunction fun(test){ console.log(test); // test}","categories":[],"tags":[{"name":"Test","slug":"Test","permalink":"https://dddreams.github.io/tags/Test/"}],"keywords":[]}]}