-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
1 lines (1 loc) · 250 KB
/
content.json
File metadata and controls
1 lines (1 loc) · 250 KB
1
[{"title":"工作中常使用的git命令","date":"2023-06-18T08:50:09.000Z","path":"2023/06/18/工作中常使用的git命令/","text":"git 作为版本控制工具中的王者,日常工作中肯定是离不开了,啥,还有使用 SVN 的,对不起,打扰了。日常工作中,常用哪些命令,本文来梳理一下。 1、基础的git clone这个命令一定是个开始,代码地址发你了,你首先做的肯定是克隆代码啊,没毛病。(啥,新项目,你需要提交第一行代码,那你需要 git init) git config代码拉取下了,嗯,开始提交代码,不,你需要配置一下用户名和邮箱,公司可是会根据这个统计的你的代码量的,你可别被认定为摸鱼了,哈哈。 git fetch –all啥,拉取的代码只有 master 分支,那你需要 fetch 一下,不然怎么基于 devlop 分支,新建分支进行开发呢? git checkout -b嗯,你有开发任务了,新建分支开发吧,直接用这个命令,新建分支并且换过去,然后就可以开发了。 git add写了不少代码,需要让 git 帮忙进行版本控制,不然为啥使用 git 呢?所以把文件放入暂存区,使用 git add。 git commit提交代码,需要这个命令。 git pull提交之前,看看别人有没有在这个分支提交内容,先拉取一下。 git push一切顺利的话,就可以往上推送代码了。当然,有时候还需要强制推送。(一般不可以,除非你知道自己在做什么) git merge代码需要合并到 devlop 然后提测,那你需要 merge 一下。 git rebase当然,如果你希望 git 提交记录整洁一下,不使用 merge 而选择 rebase,也是不错的,看各个公司的要求了。 2、高级的git stash啥,代码写了一半,测试说,测试环境,你的代码有 bug,影响测试主流程,需要尽快修复,那么你就 stash 一下,把正在编写的代码,暂存一下吧。修改完 bug,晚点再回来继续开发。 git cherry pick这个功能高级,摘樱桃,可以把你想要的提交从一个分支,迁移到另一个分支,值得一试。 git reset啥,代码弄乱了,都不要了,想和远端保持一致,嗯,你需要 reset –hard。 3、总结git 每天都在用,对于程序员,再熟悉不过的工具了,你有使用哪些 git 别名来提高效率,欢迎分享一下。","tags":[{"name":"git","slug":"git","permalink":"http://freemeng.com/tags/git/"}]},{"title":"Kafka 学习入口","date":"2023-06-04T02:23:24.000Z","path":"2023/06/04/Kafka-学习入口/","text":"网上学习资料越来越多,抄来抄去,质量是越来越差,看着免费挺好,但浪费了太多时间,就想着记录一些自己学习过程中遇到的比较好的、质量高的文档,以及工作中遇到的问题,毕竟工作那么多年。 官方文档1、Kafka 官方文档 说明:英文,注意阅读时选择相应版本。 2、SpringBoot For Apache Kafka Support 说明:现在 Java 项目大部分都是基于 SpringBoot,如何使用 SpringBoot 接入 Kafka,可以看这个官方文档,学习相关配置。 3、Spring For Apache Kafka 说明:SpringBoot For Apache Kafka Support 可以让我们快速接入 Kafka,接下来如何在 Spring 环境中发送、消费消息,还得看这个文档。 4、Spring For Apache Kafka Samples 说明:这个是官方提供的一些 Spring 集成 Kafka 的例子,个人感觉一般,可以看看。 5、Spring Boot 集成 Kafka 配置文件配置项 说明:通过 SpringBoot 集成 Kafka 后,多少要进行一些配置,那支持哪些配置项,可以看看这个。","tags":[{"name":"Kafka","slug":"Kafka","permalink":"http://freemeng.com/tags/Kafka/"}]},{"title":"JVM 相关命令实战","date":"2022-11-20T02:21:47.000Z","path":"2022/11/20/JVM-相关命令实战/","text":"最近复习了一下 JVM 相关知识,发现一些常用的命令、工具已经生疏了,遂整理成问答形式,打开 iTerm,走起~ 问1:如何查看当前 JVM 最大堆内存配置是多少?1jmap -heap 230646 问2:如何查看当前 JVM 使用的垃圾收集器是?1jcmd 230646 VM.flags 问3:如何查看当前 GC 运行情况?1jstat -gcutil 230646 1000 3 问4:如何配置应用打印 GC 日志?1-XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:./gclogs 问5:如何配置在应用堆内存溢出时,自动保存快照?1-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof 问6:如何配置应用打印类加载信息?1-XX:+TraceClassLoading 问7:用什么工具查看堆内存快照?jvisualvm、mat 问8:用什么工具查看 GC 日志?GCViewer 工具、GCEasy 网站","tags":[{"name":"JVM","slug":"JVM","permalink":"http://freemeng.com/tags/JVM/"}]},{"title":"如何写出高性能SQL语句","date":"2022-07-02T07:58:12.000Z","path":"2022/07/02/如何写出高性能SQL语句/","text":"我们平时开发离不开 SQL 语句,随着数据库中数据量的增加,慢SQL就出现了,那么如何写出高性能的SQL语句?如何优化慢SQL? 慢SQL的几种常见原因1、无索引、索引失效这种情况,很常见,也比较好排查。使用 explain 查看执行计划,然后进行索引优化。 2、数据量增大如果一个表,每日增量几十万,那用不了多久,这个表就是一个大表,MySQL 索引组织表,随着数据量增多,树的高度变高,每次查询,磁盘的IO次数变多,最后发生慢查询。这个时候针对不同业务,可以考虑,仅保留近几天数据,或者进行分表。 3、锁等待这种情况的慢查询,出现频率不固定,可能你在操作变更表的索引,然后来了一个查询,就产生了一个慢查询。因此,要在业务低峰进行数据库操作。更新语句尽量使用主键或者唯一索引。 4、不恰当的SQL语句例如 SELECT *、LIMIT M,N (大数据量时分页查询)、对非索引字段进行排序等。 优化SQL语句的步骤1、通过 EXPLAIN 分析 SQL 执行计划重点关注 key、type 两个值,确定是否走索引,以及查询使用的方式。type 为 index 是索引全表扫描,性能差,仅次于 all,尽量优化掉。 2、通过 Show Profile 分析 SQL 执行性能","tags":[{"name":"MySQL","slug":"MySQL","permalink":"http://freemeng.com/tags/MySQL/"}]},{"title":"insert 导致的死锁现场一","date":"2022-04-08T07:31:15.000Z","path":"2022/04/08/insert-导致的死锁现场一/","text":"今天来看一个 MySQL insert 死锁现场。 环境准备建表语句如下,注意这个表存在一个唯一索引。 1234567CREATE TABLE lock_test1 ( id INT NOT NULL AUTO_INCREMENT, user_name VARCHAR(45) NOT NULL, age INT NULL, PRIMARY KEY (id), UNIQUE INDEX `uniq_user_name` (user_name ASC)); 基本情况数据库版本及事务隔离级别如下。 1234-- 5.7.10-logselect @@version;-- REPEATABLE-READselect @@tx_isolation; 现场复原开启三个 session,执行同样的 insert 语句,然后 session A 执行回滚,即可复现死锁。 原因分析t2 时刻查询一下锁情况 1select * from information_schema.innodb_locks; 根据上面的锁情况,来分析一下原因。 t1 时刻,session A 执行完 insert 语句后,加了 X 锁,t2 时刻,session B 执行完 insert 语句后,等待 S 锁,同样 session C 执行完 insert 语句后,等待获取 S 锁。t3 时刻,session A 执行 rollback,释放了 X 锁,这时,session B、session C 都想加 X 锁,但是它们还都持有 S 锁,最终导致了死锁。 疑问解释 insert 语句加什么锁? INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row. MySQL 官方文档描述,insert 加 X 记录锁,非 next-key lock。 session B 为什么加 S 锁? If a duplicate-key error occurs, a shared lock on the duplicate index record is set. MySQL 官方文档描述,发生唯一键冲突,会在索引上加 S 记录锁。 总结今天的 insert 死锁现场的原因是唯一键冲突。 参考文档MySQL 5.6 Reference Manual 《MySQL 实战 45 讲》","tags":[{"name":"MySQL","slug":"MySQL","permalink":"http://freemeng.com/tags/MySQL/"}]},{"title":"InnoDB CRUD 设置了哪些锁","date":"2022-04-07T02:43:09.000Z","path":"2022/04/07/InnoDB-CRUD-设置了哪些锁/","text":"之前我们介绍了全局锁、表级锁和行锁,那么平时我们写的 CRUD ,会设置哪些锁呢? 一致性读1select ... from 一致性读,读取数据库快照,不会设置锁,除非你使用 SERIALIZABLE 隔离级别。 INSERT在插入行设置一个排他锁。这个锁是 index-record lock,不是 next-key lock。 UPDATE搜索过程中遇到的每一行都会加上 next-key lock。然后,当使用唯一索引,搜索唯一的行时,设置 index-record lock。","tags":[{"name":"MySQL","slug":"MySQL","permalink":"http://freemeng.com/tags/MySQL/"}]},{"title":"MySQL 行锁","date":"2022-04-04T02:28:11.000Z","path":"2022/04/04/MySQL-行锁/","text":"根据加锁的范围,MySQL 里面的锁大致可以分为:全局锁、表级锁、行锁三类。今天学习一下行锁。 MySQL 的行锁,是由引擎层实现的,InnoDB 支持行锁,而 MyISAM 不支持行锁,这也是 MyISAM 被 InnoDB 替代的重要原因之一。 因为支持行锁,意味着同一时刻可以支持更高的并发度。 行锁之独占和共享 共享锁(读锁,S 锁) 共享锁,允许持有锁的事务进行读取。 12select ... lock in share mode;insert into ... select ...; 以上语句会对 select 的表上,扫描到的数据加共享锁,数据行被事务添加共享锁后,其它事务可以添加共享锁,但是不能添加独占锁。 独占锁(写锁,X 锁) 独占锁,允许持有锁的事务进行更新、删除。 123update...;delete...;select... for update; 数据行被添加独占锁后,其它事务不能再为数据行添加任意类型的锁。 间隙锁间隙锁(Gap Lock)存在于可重复读隔离级别,为了解决幻读。 间隙锁是对索引记录之间的间隙的锁定。 间隙锁之间不冲突,和间隙锁冲突的是插入意向锁。 NEXT-KEY Lock 间隙锁和行锁合称 NEXT-KEY Lock,它是前开后闭区间,锁定一个范围以及记录本身。InnoDB 默认使用它来实现范围的锁定。 插入意向锁 由 insert 操作在插入行之前设置的一种间隙锁。 Predicate LocksInnoDB 支持对包含空间列的列进行 SPATIAL 索引。 为了支持具有 SPATIAL 索引的表的隔离级别,InnoDB 引入了 Predicate Locks。 两阶段锁协议在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。 如何利用? 如果你的事务中需要锁多个行,要把最有可能造成锁冲突、最有可能影响并发度的锁尽量往后放。 总结读提交隔离级别下,锁的范围更小,锁的时间更短,如果业务场景符合,建议使用读提交隔离级别。 参考文档《网易云课堂》 《MySQL 实战 45 讲》","tags":[{"name":"MySQL","slug":"MySQL","permalink":"http://freemeng.com/tags/MySQL/"}]},{"title":"后端学DOM BOM 第四天笔记","date":"2022-03-28T02:03:15.000Z","path":"2022/03/28/后端学DOM-BOM-第四天笔记/","text":"后端学习 DOM BOM 第四天笔记,学习资料为 B 站黑马程序员。 今天主要内容是本地存储。 本地存储特性 数据存储在用户浏览器中 设置、读取方便,页面刷新不丢失数据 容量较大,sessionStorage 约 5M,localStorage 约 20M 只能存储字符串,可以将对象 JSON.stringify() 编码后存储 sessionStorage 生命周期为关闭浏览器窗口 在同一个窗口下,数据可以共享 以键值对的形式存储使用 12345678// 存储数据sessionStorage.setItem(key,value);// 获取数据sessionStorage.getItem(key);// 删除数据sessionStorage.removeItem(key);// 清空数据sessionStorage.clear(); localStorage 生命周期永久生效,除非手动删除,否则关闭页面也会存在 可以多窗口共享(同一浏览器可以共享) 以键值对的形式存储使用 1234localStorage.setItem(key,value);localStorage.getItem(key);localStorage.removeItem(key);localStorage.clear(); 案例记住用户名","tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"http://freemeng.com/tags/JavaScript/"}]},{"title":"后端学DOM BOM 第三天笔记","date":"2022-03-27T12:56:31.000Z","path":"2022/03/27/后端学DOM-BOM-第三天笔记/","text":"后端学习 DOM BOM 第三天笔记,学习资料为 B 站黑马程序员。 今天主要内容是 BOM。 BOM1.BOM 概述BOM(Browser Object Model)即浏览器**对象模型**,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。 BOM 缺乏标准,JavaScript 语法的标准化组织是 ECMA,DOM 的标准化组织是 W3C,BOM 最初是Netscape 浏览器标准的一部分。 BOM 比 DOM 更大,它包含 DOM。 2.window 对象的常见事件1234567// 窗口加载事件window.onload = function(){}window.addEventListener(\"load\",function(){});document.addEventListener('DOMContentLoaded',function(){})// 调整窗口大小事件window.onresize = function(){}window.addEventListener(\"resize\",function(){}); 3.定时器123456789// 定时器一 window.setTimeout(调用函数, [延迟的毫秒数]); // 停止定时器 window.clearTimeout(timeoutID) // 定时器二(重复调用) window.setInterval(回调函数, [间隔的毫秒数]);// 停止定时器 window.clearInterval(intervalID); 4.JS 执行机制JS 是单线程。 为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步。 JS 执行机制: 1.先执行执行栈中的同步任务; 2.异步任务(回调函数)放入任务队列中 3.一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,也是任务队列中的异步任务结束等待状态,进入执行栈,开始执行。 5.location 对象window 对象给我们提供了一个 location 属性用于获取或设置窗体的 URL,并且可以用于解析 URL 。 因为这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象。 6.navigator 对象navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客户机发送服务器的 user-agent 头部的值。 12345if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) { window.location.href = \"\"; //手机 } else { window.location.href = \"\"; //电脑 } 7.history 对象window 对象给我们提供了一个 history 对象,与浏览器历史记录进行交互。该对象包含用户(在浏览器窗口中)访问过的 URL。","tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"http://freemeng.com/tags/JavaScript/"}]},{"title":"后端学DOM BOM 第二天笔记","date":"2022-03-27T11:48:31.000Z","path":"2022/03/27/后端学DOM-BOM-第二天笔记/","text":"后端学习 DOM BOM 第二天笔记,学习资料为 B 站黑马程序员。 今天主要内容是事件。 事件1.注册事件给元素添加事件称为注册事件或者添加事件。 12345678910function addEventListener(element, eventName, fn) { // 判断当前浏览器是否支持 addEventListener 方法 if (element.addEventListener) { element.addEventListener(eventName, fn); // 第三个参数 默认是false } else if (element.attachEvent) { element.attachEvent('on' + eventName, fn); } else { // 相当于 element.onclick = fn; element['on' + eventName] = fn; } 2.删除事件123456789function removeEventListener(element, eventName, fn) { // 判断当前浏览器是否支持 removeEventListener 方法 if (element.removeEventListener) { element.removeEventListener(eventName, fn); // 第三个参数 默认是false } else if (element.detachEvent) { element.detachEvent('on' + eventName, fn); } else { element['on' + eventName] = null; } 3.DOM 事件流事件流描述的是从页面中接收事件的顺序。 事件发生时,会在元素节点之间按照特定的顺序传播,这个传播过程即 DOM 事件流。 4.事件对象事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象 event,它有很多属性和方法。 1e = e || window.event; 5.阻止事件冒泡12345if(e && e.stopPropagation){ e.stopPropagation(); }else{ window.event.cancelBubble = true; } 6.事件委托(代理、委派)7.常用的鼠标事件8.常用的键盘事件keydown->keypress->keyup","tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"http://freemeng.com/tags/JavaScript/"}]},{"title":"后端学DOM BOM 第一天笔记","date":"2022-03-27T09:04:22.000Z","path":"2022/03/27/后端学DOM-BOM-第一天笔记/","text":"后端学习 DOM BOM 第一天笔记,学习资料为 B 站黑马程序员。 今天主要内容是 Web APIs、DOM 。 Web APIs 简介1.Web APIs 和 JS 基础关联性 JavaScript 由 JavaScript 基础、Web APIs 组成。 2.API 和 Web APIWeb API 是浏览器提供的一套操作浏览器功能和页面元素的 API( BOM 和 DOM),主要针对浏览器做交互效果。 DOM1.DOM 简介文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口。 W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。 文档:一个页面就是一个文档,DOM 中使用 document 表示。 元素:页面中的所有标签都是元素,DOM 中使用 element 表示。 节点:网页中所有内容都是节点(标签、属性、文本、注释等),DOM 中使用 node 表示。 2.获取元素1234567891011121314// 根据 id 获取document.getElementById('id');// 根据标签名获取document.getElementByTagName('标签名');element.getElementByTagName('标签名');document.getElementsByClassName('类名');document.querySeletor('选择器');document.querySeletorAll('选择器');// 获取 body 元素document.body;// 获取 html 元素document.documentElement; 事件基础JavaScript 使我们有能力创建动态页面,而事件是可以被 JavaScript 侦测到的行为。 事件三要素:事件源、事件类型、事件处理程序 操作元素JavaScript 的 DOM 操作可以改变网页内容、结构和样式,我们可以利用 DOM 操作元素来改变元素里面的内容 、属性等。 操作元素是 DOM 的核心内容。 节点操作利用节点层级关系获取元素更简单。 一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。 我们在实际开发中,节点操作主要操作的是元素节点 1234567891011121314151617// 父节点node.parentNode// 子节点// 返回所有子节点,包括元素节点、文本节点等parentNode.childNodes(标准)// 只返回子元素节点parentNode.children(非标准,常用重点掌握)parentNode.firstChildparentNode.lastChild// 创建节点document.createElement('tagName')// 添加节点node.appendChild(child)// 删除节点node.removeChild(child)// 复制节点node.cloneNode()","tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"http://freemeng.com/tags/JavaScript/"}]},{"title":"后端学JS第六天笔记","date":"2022-03-27T05:43:51.000Z","path":"2022/03/27/后端学JS第六天笔记/","text":"后端学习 JavaScript 第六天笔记,学习资料为 B 站黑马程序员。 今天主要内容是内置对象、简单类型与复杂类型。 内置对象1.内置对象JavaScript 对象分为:自定义对象,内置对象,浏览器对象。 内置对象的优点就是帮组我们快速开发。 2.查文档https://developer.mozilla.org/zh-CN/ 3.Math 对象123456Math.PI // 圆周率Math.floor() // 向下取整Math.ceil() // 向上取整Math.round() // 四舍五入版 就近取整 注意 -3.5 结果是 -3 Math.abs() // 绝对值Math.max()/Math.min() // 求最大和最小值 4.日期对象Date 对象是一个构造函数,实例化后才能使用。Date 用来处理日期和时间。 12var now = new Date();console.log(now); 5.数组对象创建数组的第二种方式 1new Array(); instanceof 运算符,可以判断一个对象是否属于某种类型,Array.isArray() 用于判断一个对象是否是数组。 123456var arr = [1, 23];var obj = {};console.log(arr instanceof Array); // trueconsole.log(obj instanceof Array); // falseconsole.log(Array.isArray(arr)); // trueconsole.log(Array.isArray(obj)); // false 添加删除数组元素的方法: 12345678// 末尾添加一个或多个元素push(参数1...);// 末尾删除一个元素pop();// 开头添加一个或多个元素unshift(参数1...);// 开头删除一个元素shift(); 数组排序: 1234// 颠倒数组中元素顺序reverse();// 对数组中元素排序sort(); 6.字符串对象基本包装类型就是把简单数据类型包装成复杂数据类型,这样基本数据类型就有了属性和方法。 字符串不可变。 API 不作记录,需要时查看文档。 简单类型与复杂类型1.简单类型与复杂类型简单类型又叫基本数据类型或者值类型,复杂类型又叫引用类型。 简单类型:string、number、boolean、 undefined、null 引用类型:Object、Array、Date 等 2.栈和堆简单数据类型存放到栈里面,复杂数据类型存放到堆里面。 JavaScript 中没有堆栈的概念。 3.简单类型的内存分配值类型变量的数据直接存放在变量(栈空间)中。 4.复杂类型的内存分配引用类型变量(栈空间)存放的是地址,真正的对象实例存放在堆空间中。 5.简单类型传参函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量。 6.复杂类型传参函数的形参也可以看做是一个变量,当我们把引用类型变量传给形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象。 总结JavaScript 的内置对象,和 Java 一样,就是一些 API,方便快速开发程序。 简单类型和复杂类型,就是值类型和引用类型。 ###","tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"http://freemeng.com/tags/JavaScript/"}]},{"title":"后端学JS第五天笔记","date":"2022-03-27T02:13:23.000Z","path":"2022/03/27/后端学JS第五天笔记/","text":"后端学习 JavaScript 第四天笔记,学习资料为 B 站黑马程序员。 今天主要内容是作用域、预解析、JavaScript 对象。 作用域1.作用域es6 之前有包括全局作用域和局部作用域(函数作用域),没有块级作用域。 2.变量的作用域全局变量和局部变量。 全局变量:在任何地方都可以使用,只有浏览器关闭时才会销毁,因此比较占内存。 局部变量:只在函数内部使用,当其所在代码块被执行时,会被初始化;当代码块运行结束后,就会被销毁,因此更节省内存空间。 3.作用域链采取就近原则的方式来查找变量最终值。 预解析1.预解析JavaScript 代码是由浏览器中 JavaScript 解析器来执行的。 运行 JavaScript 代码分两步:预解析和代码执行。 预解析:在当前作用域下,JS 代码执行之前,浏览器会默认把带有 var 和 function 声明的变量在内存中进行提前声明和定义。 2.变量预解析和函数预解析预解析也叫变量、函数提升。 变量提升:变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。 函数提升:函数的声明会被提升到当前作用域的最上面,但是不会调用函数。 JavaScript 对象1.对象对象由属性和方法组成,和 Java 语音中对象一样。 2.创建对象的三种方式1)利用字面量创建对象(就是用{}) 12345678var star = { name : 'pink', age : 18, sex : '男', sayHi : function(){ alert('大家好啊~'); }}; 对象的调用 123console.log(star.name) // 调用名字属性console.log(star['name']) // 调用名字属性star.sayHi(); // 调用 sayHi 方法,注意,一定不要忘记带后面的括号 2)利用 new Object 创建对象 1234567var andy = new Obect();andy.name = 'pink';andy.age = 18;andy.sex = '男';andy.sayHi = function(){ alert('大家好啊~');} 3)利用构造函数创建对象 123456789101112function Person(name, age, sex) { this.name = name; this.age = age; this.sex = sex; this.sayHi = function() { alert('我的名字叫:' + this.name + ',年龄:' + this.age + ',性别:' + this.sex); }}var bigbai = new Person('大白', 100, '男');var smallbai = new Person('小白', 21, '男');console.log(bigbai.name);console.log(smallbai.name); 和 Java 很像了,JS 各种杂糅。 3.new 关键字new 在执行时会做四件事情: 1.在内存中创建一个新的空对象; 2.让 this 指向这个新对象; 3.执行构造函数里的代码,给这个新对象添加属性和方法; 4.返回这个新对象。 4.遍历对象属性for…in 语句用于对数组或者对象的属性进行循环操作。 1234for (var k in obj) { console.log(k); // 这里的 k 是属性名 console.log(obj[k]); // 这里的 obj[k] 是属性值} 总结作用域和 Java 语言不同的地方是,JS 没有块级作用域。 预解析,是一个特点,也叫变量、函数提升,解释了 JS 为什么可以先使用变量或者函数后定义。 对象和 Java 语言差不多,有个 for…in 语法,构造函数为类的概念。","tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"http://freemeng.com/tags/JavaScript/"}]},{"title":"后端学JS第四天笔记","date":"2022-03-27T01:21:50.000Z","path":"2022/03/27/后端学JS第四天笔记/","text":"后端学习 JavaScript 第四天笔记,学习资料为 B 站黑马程序员。 今天主要内容是数组和函数。 数组1.数组的概念2.创建数组定义数组 1234// 方法一var arr = [1,2,true,'abc'];// 方法二var arr = new Array(); 注意: 1、数组中可以存放任意类型数据 3.获取数组元素1alert(arr[1]); 4.遍历数组1234var arr = ['red','green', 'blue'];for(var i = 0; i < arr.length; i++){ console.log(arrStus[i]);} 5.数组中新增元素123var arr = ['red', 'green', 'blue', 'pink'];arr[4] = 'hotpink';console.log(arr); 函数1.函数的概念2.函数的使用声明函数 123function 函数名(){ // 函数体代码} 注意:函数名一般为动词,例如:getSum 使用函数 1函数名(); 3.函数的参数12345678// 函数声明function getSum(num1, num2){ console.log(num1 + num2);}// 调用函数getSum(1,2);getSum(6,5) 注意: 1、形参不用声明,也没有类型一说 2、形参、实参个数不一致也可以正常调用 3、形参的默认值是 undefined 4.函数的返回值123456// 声明函数function getSum(){ return 666;}// 调用函数getSum(); 注意: 1.函数定义时,不用声明返回值类型,JS 是弱类型的 2.函数都有返回值,没有 return 返回 undefined 5.arguments 的使用arguments 是函数的内置对象。 1234567891011function maxValue() { var max = arguments[0]; for (var i = 0; i < arguments.length; i++) { if (max < arguments[i]) { max = arguments[i]; } } return max;} console.log(maxValue(2, 4, 5, 9)); console.log(maxValue(12, 4, 9)); 6.函数案例7.函数的两种声明方式自定义函数方式(命名函数) 调用函数的代码,既可以写到函数声明前面,也可以写到函数声明后面。 1234function fn(){ }fn(); 函数表达式方式(匿名函数) 函数声明类似变量声明,调用函数的代码,必须写到函数体后面。 12var fn = function(){};fn(); 总结数组和函数,Java 中也有。 数组两者之间差别不大。JS 数组里可以放不同类型数据,毕竟 JS 是弱类型。 函数方面,JS 函数定义使用关键字 function ,加上由于 JS 是弱类型,形参部分不用声明类型,返回值类型同样不用申明,还有 JS 函数内置了 arguments 对象。","tags":[{"name":"JavaScript","slug":"JavaScript","permalink":"http://freemeng.com/tags/JavaScript/"}]},{"title":"MySQL 表级锁","date":"2022-03-25T07:09:24.000Z","path":"2022/03/25/MySQL-表级锁/","text":"根据加锁的范围,MySQL 里面的锁大致可以分为:全局锁、表级锁、行锁三类。今天学习一下表级锁。 MySQL 表级锁有两种:一种是表锁,一种是元数据锁(meta data lock,MDL)。 表锁加锁1lock tables ... read/write 解锁1unlock tables 应用场景对于不支持行锁的引擎(MyISAM 仅支持表锁),使用它控制并发 意向锁意向锁是一种协作机制,用于表锁和行锁的共生场景。(InnoDB 引擎) 意向共享锁(IS):事务想获得表中某些记录的共享锁,需要在表上先加意向共享锁。 意向互斥锁(IX):事务想获得表中某些记录的互斥锁,需要在表上先加意向互斥锁。 自增锁(autoinc_lock)autoinc_lock 是一种特殊的表级锁,和 autoinc_lock、表级 S 锁和 X 锁不相容。 autoinc_lock 加锁逻辑,受 indodb_autoinc_lock_mode 控制。 模式 0-传统锁定模式 语句执行结束后才释放自增锁。 模式 1-自动模式(默认) 普通 insert 语句,自增锁在申请之后马上释放。 类似 insert…select 语句,自增锁在语句结束之后才释放。 模式 2 所有 insert 语句都在申请后马上释放。 元数据锁(MDL 锁)MDL 锁,分为 MDL 读锁和 MDL 写锁,读锁和读锁不冲突,读锁和写锁互斥,写锁和写锁互斥。 加锁在访问一个表的时候会被自动加上。 当对一个表做增删改查操作的时候,申请 MDL 读锁。 当对表结构进行变更的时候,申请 MDL 写锁,获取写锁时需要等待读锁释放,且申请写锁会阻塞后续所有 MDL 锁的获取。 解锁事务中的 MDL 锁,在语句执行开始时申请,但是语句结束后并不会马上释放,而会等到整个事务提交后再释放。 应用场景系统自动使用,用于避免 DDL 和 DML 并发冲突。 总结MySQL 的表级锁主要有两种,第一个是表锁,注意特殊的意向锁、自增锁。 第二个是元数据锁,由于系统默认加锁、解锁,平时可能感觉不到,但是操作表结构时,务必注意这个锁。 参考《MySQL 实战 45 讲》 《SQL必知必会》 《网易云课堂》","tags":[{"name":"MySQL","slug":"MySQL","permalink":"http://freemeng.com/tags/MySQL/"}]},{"title":"MySQL 全局锁","date":"2022-03-24T07:01:21.000Z","path":"2022/03/24/MySQL-全局锁/","text":"根据加锁的范围,MySQL 里面的锁大致可以分为:全局锁、表级锁、行锁三类。今天学习一下全局锁。 什么是全局锁全局锁就是对整个数据库实例加锁。 如何操作全局锁加全局读锁: 1flush tables with read lock; Closes all open tables and locks all tables for all databases with a global read lock. 解锁: 1unlock tables; 加了全局锁后,什么现象整个数据库实例处于只读状态。 其它线程的数据更新语句(数据的增删改)、数据定义语句(建表、修改标结构)均会阻塞。 当前线程的数据更新语句(数据的增删改)、数据定义语句(建表、修改标结构)均直接报错(have a conflicting read lock)。 全局锁的使用场景数据库全库逻辑备份(针对数据库中有使用 MyISAM 等不支持事务引擎的表) 拓展物理备份:对数据库系统的物理文件(如数据文件,日志文件等)的备份,也可说是文件系统级别的备份,其中包含的方法:冷备份(脱机备份):是在关闭数据库的时候进行的;热备份(联机备份):数据库处于运行状态,依赖于数据库的日志文件;温备份:数据库锁定表格(不可写入但可读)的状态下进行备份操作。 逻辑备份:就是对数据库逻辑组件(如:表等数据库对象)的备份,备份文件是 SQL 文件或特定格式的导出文件。 实际操作中大部分以物理备份为主,逻辑备份为辅。逻辑备份和物理备份各有优劣,一般来说,物理备份恢复速度比较快,但占用空间比较大,逻辑备份速度比较慢,占用空间比较小,但逻辑备份的恢复成本相对高一些; 总结如果表使用了不支持事务的引擎,那么你需要全局锁,否则用不到。 参考MySQL 5.7 Reference Manual 《MySQL 实战 45 讲》 《数据库与其备份恢复主要注意事项》","tags":[{"name":"MySQL","slug":"MySQL","permalink":"http://freemeng.com/tags/MySQL/"}]},{"title":"MySQL 索引快问快答","date":"2022-03-22T08:48:07.000Z","path":"2022/03/22/MySQL-索引快问快答/","text":"MySQL 索引的 12 个小问题 1、Like 会不会走索引% 在前的模糊匹配不走索引 2、索引列能不能为空可以,但是当为空数据比较少的时候,查询 is not null 会全表扫描 3、函数计算会不会走索引一般不会,另外需要注意,执行计划中的 type = index ,这个查询效率不高,是全索引扫描,虽然用到了索引,但是是索引扫描,不是索引搜索,效率比较低 4、类型不一致会不会走索引具体问题具体分析,不一定 5、where 条件顺序怎么写记住最左前缀,其他不重要,另外一个查询是可以用到多个索引的,例如:使用 OR 连接的多个单列索引查询 6、要不要使用 union 替换 or可以,但是 SQL 变复杂了,另外如果后面分库分表,是否支持需要考虑一下 7、EXISTS vs IN子查询结果少用 in,结果多用 exists。 8、非等于是否走索引一般不走,特殊情况是:主键或 count(*)会走 9、索引覆盖尽量不要使用 select * 10、用子查询还是表关联大部分子查询都可以用表关联替换,建议使用表关联 11、表关联之大小表优化器,自己可以优化,不用关注是大表关联小表,还是小表关联大表 12、分页的玩法深分页较好方案就是子查询 + 索引覆盖","tags":[{"name":"MySQL","slug":"MySQL","permalink":"http://freemeng.com/tags/MySQL/"}]},{"title":"MySQL explain 精简说明","date":"2022-03-20T08:23:18.000Z","path":"2022/03/20/MySQL-explain-精简说明/","text":"本篇文章主要内容是 MySQL 优化器的执行计划说明,侧重基础概念介绍。 MySQL 的 Server 层包括:连接器、分析器、优化器、执行器。 分析器->做什么,优化器->怎么做。 优化器在表里面有多个索引的时候,决定使用哪个索引。 优化器在一个语句有多表关联(join)的时候,决定各个表的连接顺序。 查看 SQL 执行计划语法:explain+SQL 1explain select * from ecs_order_info where order_id <1000; 查询结果如下 接下来的目标,就是读懂这个查询结果。 执行计划输出列说明下图为执行计划输出列及其解释,重点关注 type、key、rows、extra 列。 关键字段-select_typeselect_type 列常见的值及解释见下图,简单了解即可。 关键字段-type(access_type)type 列常见的值及解释见下图,这个列要重点掌握,下表中从上到下性能依次下降。All 是全表扫描,性能最差的一种类型,同样 index 仅比它好一点。 关键字段-extraextra 列常见值及解释如下图,这个列可以为我们提供额外的信息,例如是否使用临时表、是否使用外部排序等等。 总结通过 explain 我们可以查看 SQL 的执行计划,用于排查慢 SQL 。 参考《网易云课堂-Java高级架构师》 《极客时间-MySQL实战45讲》","tags":[{"name":"MySQL","slug":"MySQL","permalink":"http://freemeng.com/tags/MySQL/"}]},{"title":"收尾总结-笔记","date":"2022-03-18T01:35:54.000Z","path":"2022/03/18/收尾总结-笔记/","text":"最近在学习网易云课堂项目管理,记录一下。 经过共同努力(加班),项目终于完成了,这时项目到了收尾阶段。收尾阶段有三个重要工作:完成验收、更新归档和复盘总结。 完成验收获得客户或发起人对项目最终产品、服务或成果的验收。 如果交付的是内部项目,完成验收这项工作,可能没有那么正式,不会有验收报告。产品经理在收到邮件(测试同学发送的线上回归通过)后,回复一个验收通过,就结束了。 更新归档项目管理过程中,文档必不可少,这些文档是项目的资产,提高了项目的可维护性。没有人愿意接受一个没有任何文档的项目。 文档很重要的一点是,记得更新,没有及时更新的文档,是没有价值的,已经反应不了实际情况。 启动 市场调研方案、启动会会议纪要 规划 进度计划、沟通计划、风险列表、里程碑计划 执行 项目会议纪要、代码、技术方案、测试用例、设计稿、视觉稿 监控 变更记录 收尾 验收报告、总结报告 复盘总结收尾阶段最重要的工作就是复盘,通过反思复盘,才能积累经验,提高自己的项目管理能力。 回顾目标 项目目标是什么? 评估现状 项目实际结果是怎样的? 跟计划相比,哪些符合预期? 跟计划相比,哪些未达预期? 分析过程 符合预期,做对了什么? 未达预期,发生了什么? 总结经验 从过程中学到了什么新东西? 如果重来一遍会怎么做? 要停止做什么? 要开始做什么? 要继续做什么? 总结善始善终,项目到了收尾阶段,也不要放松,认真复盘总结,积累经验。 行百步者半九十。","tags":[{"name":"项目管理","slug":"项目管理","permalink":"http://freemeng.com/tags/项目管理/"}]},{"title":"基金小知识","date":"2022-03-17T12:21:48.000Z","path":"2022/03/17/基金小知识/","text":"记录一些基金相关的小知识。 单位净值指总资产减去总负债,再除以基金份额总数。 通常,新成立的基金,单位净值都是 1 元。 累计净值在单位净值的基础上累加了这只基金成立以来的累计分红和拆分的金额。 基金分红当一只基金没有太好的投资机会,又不希望账上太多资金闲置时,把基金的现金返还投资者的一种操作。 基金估值基金平台根据公开披露的基金持仓和指数走势估算出来的参考值。 基金分类(底层资产)股票基金、混合基金、债券基金、货币基金 基金分类(投资策略)主动基金、指数基金 A 类份额通常是前端收费,在买入时,一次性收取认购费/申购费。不收销售服务费。金额较大,且投资周期 > 1 年,考虑 A 类。 C 类份额收取销售服务费。不收取认购费/申购费。持有时间 < 1 年,可以考虑 C 类。 场内基金指在证券公司开通股东账户,可在证券公司营业或者证券公司的专门网站进行上市交易型开放式基金LOF与ETF基金的场内交易。手续费更低。 场外基金指每天只有一个净值作为申购赎回的价格,也可以叫做普通开发基金。场外,顾名思义,可以理解为在股票基金交易市场外的银行、证券公司代销。 LOF全称上市型开放式基金(Listed Open-Ended Fund)是开放式基金的一类,基金份额不固定。可以场内、场外买卖。 ETF全称交易所交易型基金,可以在场内像股票一样交易的基金。 比如你看好消费行业,但不会选个股,就可以卖消费 ETF。 ETF 联接基金指将绝大部分基金资产投资于跟踪同一标的的指数的 ETF 基金,进而密切跟踪标的指数表现。配置了少部分债券和现金用于应对赎回。 QDII是指在一国境内设立,经该国有关部门批准从事境外证券业务的证券投资基金。投资者可以通过投资 QDII 基金间接参与境外市场投资。","tags":[{"name":"基金","slug":"基金","permalink":"http://freemeng.com/tags/基金/"}]},{"title":"项目执行-笔记","date":"2022-03-17T02:41:36.000Z","path":"2022/03/17/项目执行-笔记/","text":"最近在学习网易云课堂项目管理,记录一下。 执行到位就是竞争力! 项目推进做什么 设置检查点 建立进展同步机制 识别偏差 评估影响 采取纠偏措施 结论信息同步 理想和现实总是有差距的。项目的实际进展不会和我们项目计划完全一致,推进过程中,可能会发生人员变动、插入任务、客户需求变更、公司战略调整等等意外情况。 为了发现这样的情况,我们需要设置检查点及建立同步机制。每个里程碑节点,就是很好的检查点,在里程碑节点,检查项目进展。 站立会、周会、日报、周报等就是一个很好的同步机制。当发生偏差,遇到阻碍时,一定要及时提出,然后评估影响,采取相应措施,以达到纠偏,保证项目按期进行。 如何应对变更 Record-记录 可追溯、可查询 Revaluate-评估 对目标的影响 React-应对 拒绝、延后、实施 Renew-更新 计划更新、信息同步 Review-复盘 原因分析、改进措施、经验沉淀 有人说变更是事故的来源,因为变更导致的事故历历在目。如何更好的应对变更?可以试试上面的 5R。 高效开会三步走 会前准备 6 确定:会议目的、会议方式、会议议程、会议时间、会议地点、与会人 会中控场 按时开会、明确会议规则、明确会议角色人、按照议程来走、及时纠偏、阶段性总结 会后跟进 会议纪要、执行跟踪 结构化汇报 5 要素 明确目的 突出重点 说清问题 罗列方案 请求支持 总结推进项目,按计划进行,有效地应对发生的变更,高效开会,及时有效地进行汇报。 执行到位,就是竞争力。","tags":[{"name":"项目管理","slug":"项目管理","permalink":"http://freemeng.com/tags/项目管理/"}]},{"title":"管理项目风险-笔记","date":"2022-03-16T00:37:11.000Z","path":"2022/03/16/管理项目风险/","text":"最近在学习网易云课堂项目管理,记录一下。 一般认为风险管理,主要包括四个步骤:风险识别、风险评估、风险应对、风险监控。 风险识别首先我们要有风险意识,风险就是活动或事件消极的、人们不希望的后果发生的潜在可能性。 在项目实施过程中,存在各种风险。例如上线延期、环境准备异常等。那么有什么好的识别风险的方法么? 核对表 组织沉淀下来的经验。举例:在做一些生产变更时,技术部一般会有 check list,需要我们逐项确认,这个 check list 就是核对表。 头脑风暴 有些情况,可能是第一次,之前没有经验,这个时候头脑风暴可以作为一个选择,之后根据实际情况形成我们的核对表,留作以后做参考。 风险评估识别了风险之后,我们需要对风险进行评估。类比干系人管理,在确认干线人之后,我们使用“权力利益四象限”对干系人进行分类,然后采取不同的管理措施。 风险也是,识别风险后,同样需要对其进行分类,我们称之为评级。 综合发生概率和影响,将风险分为重大风险、高风险、中风险和低风险。 风险应对 回避(改变计划,消除风险) 减轻(降低发生概率或影响) 接受(不采取应对措施) 转移(责任转移给第三方) 风险监控风险监控就是对项目风险的监视和控制。 风险监控就是在风险事件发生时,实施预定的应对措施。另外,当发生变化时,要重新进行风险分析并制定新的应对措施。 风险监控应是一个动态的过程。 总结风险无处不在,风险管理,主要包括四个步骤:风险识别、风险评估、风险应对、风险监控。","tags":[{"name":"项目管理","slug":"项目管理","permalink":"http://freemeng.com/tags/项目管理/"}]},{"title":"项目计划-笔记","date":"2022-03-15T03:18:02.000Z","path":"2022/03/15/项目计划-笔记/","text":"最近在学习网易云课堂项目管理,记录一下。 进度计划先看一下进度计划的步骤 如果去和实际工作安排比较的话,对于我来说识别可投入时间是需要加强的步骤。国内互联网,加班风气盛行,工时的 1 人/日,代表几个小时呢?讲师说外企 1 人/日,代表 5 个小时。 平时排工时,一个任务 5 人/日,那么五天就可以完成么?这个时间是已经包含了日常会议等其他工作占用的时间么? 目前各大公司都在使用项目协作工具,我在网上看到一个榜单《36氪企服点评| 2021项目管理软件排名》。 沟通计划在正确的时间,向正确的人,用正确的方式,提供正确的信息,并使信息产生正确的影响。这是制定沟通计划的目的。 制定沟通计划的步骤 识别需求 沟通计划 达成共识 有人的地方,就有江湖。项目的不同干系人,对项目的关注点不同,我们需要与干系人沟通,明确需要,制定沟通计划,最后一定要再次确认,达成共识。 项目协作工具,为干系人提供了随时查看项目状态的工具,我们也可以利用协作工具,去为不同干系人,定制面板,满足其需求。 沟通计划四要素 沟通对象 沟通内容 沟通频次 沟通工具 项目协作工具、每日站立会、周报、月度总结等等沟通形式,实现了不同需求的项目沟通。面对面的方式,会提高项目成员的参与感。 疫情肆虐,居家办公,少了面对面的交流,也许不会影响项目的进度,但是多少会减少员工对公司的归属感,以及和同事的合作感。 总结谋定而动,方向比努力重要。做好进度计划和沟通计划,不论什么形式,有计划,坚持执行,好结果还会远么?嗯,我去搞个加仓计划吧。","tags":[{"name":"项目管理","slug":"项目管理","permalink":"http://freemeng.com/tags/项目管理/"}]},{"title":"识别项目干系人-笔记","date":"2022-03-14T03:19:23.000Z","path":"2022/03/14/识别项目干系人-笔记/","text":"最近在学习网易云课堂项目管理,记录一下。 有人的地方,就有江湖。 干系人干系人指积极参与项目或其利益受到项目执行或完成情况影响的个人或组织。 概括来讲就是影响项目和受项目影响的人或组织。 干系人识别 3 步走识别分析根据干系人的定义去识别干系人,软件行业项目中常见的干系人: 归类应对明确了干系人之后,我们需要对干系人进行归类,使用“权利利益四象限”。 根据干系人的权利的高低和获得的利益的高低,对干系人进行分类。 高权力高利益(重点管理) 每周同步一下项目进度,项目重要问题请示汇报。 高权力低利益(令其满意) 各种项目计划、进度、风险要通知到。 高利益低权利 任何变动,要随时通知到。 低利益低权利 有相关事项时积极主动沟通。 职责分工一个和尚挑水吃,两个和尚抬水吃,三个和尚没水吃。使用 RACI 责任分配矩阵,明确职责分工。 R-Responsible-谁负责 即负责执行任务的角色,他具体负责操控项目,解决问题。 A-Accountable-谁批准 即对任务负全责的角色,只有经过他的同意和签署之后,项目才得以进行。 C-Consulted-咨询谁 拥有完成项目所需的信息或能力的人,属于辅助人员。 I-Informed-通知谁 应及时得到通知的人员。 职责分工要注意:负责人有且只能有一个,不能有过多的 C 或 I,任务分配要均衡。 总结有人的地方,就有江湖。 识别项目干系人,对干系人利用“权利利益四象限”进行归类,分别管理。 使用 RACI 责任分配矩阵,明确职责分工。","tags":[{"name":"项目管理","slug":"项目管理","permalink":"http://freemeng.com/tags/项目管理/"}]},{"title":"明确项目目标-笔记","date":"2022-03-14T00:26:02.000Z","path":"2022/03/14/明确项目目标-笔记/","text":"最近在学习网易云课堂项目管理,记录一下。 定义问题比解决问题更重要。 学生生涯中,有一种作文形式,材料作文,需要理解材料内容,自选角度,完成作文。如果对材料的中心思想理解有偏差,选择了一个比较偏的角度,那么完成的作文很可能就跑题了。 确定材料作文的中心思想就是定义问题,定义问题比解决问题更重要。 定义问题即确定目标,解决问题即实现目标。确定目标比实现目标更重要。 设定目标的原则来上大名鼎鼎的 SMART 目标管理原则。 Specific 目标必须是具体的 Measurable 目标必须是可以衡量的 Attainable 目标必须是可以达到的 Relevant 目标必须和其他目标具有相关性 Time-bound 目标必须具有明确的截止期限 关于我很疑惑的 Relevant,目标必须和其他目标具有相关性,我在网上找到一个解释,整体来说,我们每一个人的大目标就是成为更好的自己。因此你的目标要和这个大目标相关。 如何明确项目目标来继续上 5W2H 分析法。 Why(为什么) 为什么要做这个项目? 为什么之前不做,现在要做? 为什么是我们来做? What(做什么) 项目包含哪些功能? 项目的成功标准是什么? When(何时) 什么时候开始? 什么时候完成? Where(何地) 项目从哪里开始实施? 影响哪几个环境的客户? Who(谁) 哪些人会影响项目的交付? 有哪些人支持这个项目? How(怎么做) 怎么申请资源? 遇到棘手问题,可以找谁? How much(多少钱) 预算有多少? 我们要做到什么程度? 用户量什么级别? 总结通过 5W2H 明确项目目标,最后制定的项目要符合 SMART 原则。 思考通过 5W2H 明确项目目标合适么?","tags":[{"name":"项目管理","slug":"项目管理","permalink":"http://freemeng.com/tags/项目管理/"}]},{"title":"开好启动会-笔记","date":"2022-03-13T08:29:42.000Z","path":"2022/03/13/开好启动会-笔记/","text":"最近在学习网易云课堂项目管理,记录一下。 今天的主题是启动会,在我的印象中,参加过两次启动会。会议流程已经记不清楚了,只模糊记得参加的人很多,我也就凑个热闹。 言归正传,俗话说 “好的开始,是成功的一半”,可见一个好的开始的重要性。 启动会的作用 造声势 启动会是项目经理在立项后,召开的一次全体会议,部分领导受邀参会并讲话。可见,参会的领导职位越高,那么这个声势造的越大。启动会让我想起了各家的发布会,例如小米发布会,提到小米汽车,声势浩大。 造声势的作用就是告诉大家项目开始了,领导很重视,大家能参与这个项目是很荣幸的,要积极配合工作。 定调子 项目开始了,就要有方向。做什么?为什么做?何时开始做?怎么做?等等都是要确定的。清楚背景,明确方向,大家才能配合的好,完成的好。 立规矩 无规矩不成方圆,为了项目按计划完成,少不了立规矩。流程机制、责任分工、奖惩制度要明确清楚。 启动会流程 启动会流程中,开始和结束分别要有领导开场和领导总结,提问&回答环节是一个成功的启动会不可缺失的。 启动会注意事项1、全员参加 2、提前发送会议邀请 3、节奏要简洁明快 4、内容会前沟通确认 5、氛围要正式开放","tags":[{"name":"项目管理","slug":"项目管理","permalink":"http://freemeng.com/tags/项目管理/"}]},{"title":"项目管理基础概念学习笔记","date":"2022-03-13T01:38:42.000Z","path":"2022/03/13/项目管理基础概念学习笔记/","text":"最近在学习网易云课堂项目管理,记录一下。 今天学习一下基础概念。 项目万事皆项目 项目管理尽一切手段管理好项目中的人、事、物,达成项目目标 5大过程组 启动 千里之行,始于足下 规划 运筹帷幄,决胜千里 执行 言出必行,行必有果 监控 审时度势,沉着应变 收尾 慎终如始,如履薄冰 10大知识领域额全面考虑问题的思维框架 范围管理 做什么? 进度管理 什么时候做? 成本管理 花多大代价做? 质量管理 做到多好? 资源管理 需要什么? 采购管理 需要什么? 沟通管理 如何沟通? 风险管理 有可能出什么错? 整合管理 如何达到 ROI 最大化? 干系人管理 如何搞好关系? 项目铁三角项目管理的本质是平衡关系 范围 成本 时间","tags":[{"name":"项目管理","slug":"项目管理","permalink":"http://freemeng.com/tags/项目管理/"}]},{"title":"项目管理导学课-笔记","date":"2022-03-12T12:53:09.000Z","path":"2022/03/12/项目管理导学课-笔记/","text":"最近在学习网易云课堂项目管理,记录一下。 提到项目管理,我首先想到的是 PMP 项目管理专业人士资格认证,目前没有考虑去考这个证。但是工作多年,个人感觉学一些项目管理知识,对自己的职业发展有好处。 来看看吸引人的标题,《学项目管理,有序掌控你的工作生活》。 记得之前在《只管去做》这本书看到了焦虑者三问: 1、总觉得这不是自己想要的,但是又不知道自己想要什么? 2、知道自己想要什么,但不知道怎么实现? 3、知道怎么实现,但就是做不到 我在想,项目管理是不是可以解决,以项目管理的方式去管理人生目标。但前提还是得知道自己想要什么? 为什么学习项目管理 万事皆项目 学而优则仕 Java 中一切皆对象,这里万事皆项目。我学习项目管理,一是目前有空闲时间,二是学一些管理如果不能让工作生活更有序,也会拓宽后面的职业发展道路。好好学习,天天向上。 学习学习金字塔:不同学习方式,知识留存率不同。 1万小时定律:要不断地学习、实践。 学而不思则罔,思而不学则怠。","tags":[{"name":"项目管理","slug":"项目管理","permalink":"http://freemeng.com/tags/项目管理/"}]},{"title":"拾起JDBC学点东西","date":"2022-03-10T12:01:30.000Z","path":"2022/03/10/拾起JDBC学点东西/","text":"JDBC 对于 Java 开发者都不陌生,相信大家用 Java 语言第一次连接数据库就是用的它。今天我们再来看看 JDBC,学点东西。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253@Test public void testJDBC() { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { // 加载 mysql 数据库驱动 Class.forName(\"com.mysql.jdbc.Driver\"); // 获取数据库连接 connection = DriverManager.getConnection(\"jdbc:mysql://127.0.0.1:3306/jdbc-springboot\", \"root\", \"admin\"); // sql语句 String sql = \"select * from user where userName = ?\"; // 预处理 statement preparedStatement = connection.prepareStatement(sql); // 设置参数,针对sql中占位符中 preparedStatement.setString(1, \"张三\"); // 发起查询 resultSet = preparedStatement.executeQuery(); User user = new User(); // 遍历查询结果集 while (resultSet.next()) { int id = resultSet.getInt(\"id\"); String userName = resultSet.getString(\"userName\"); int userAge = resultSet.getInt(\"userAge\"); int sex = resultSet.getInt(\"sex\"); String userAddress = resultSet.getString(\"userAddress\"); // 封装结果为User对象 user.setId(id); user.setUserName(userName); user.setSex(sex); user.setUserAge(userAge); user.setUserAddress(userAddress); } System.out.println(user); } catch (Exception e) { e.printStackTrace(); } finally { try { // 释放资源 if (resultSet != null) { resultSet.close(); } if (preparedStatement != null) { preparedStatement.close(); } if (connection != null) { connection.close(); } } catch (Exception e) { e.printStackTrace(); } } } SSL 连接获取数据库连接传入的 url 如下: 1jdbc:mysql://127.0.0.1:3306/jdbc-springboot 运行单元测试我们可以得到这样的警告: Thu Mar 10 20:46:15 CST 2022 WARN: Establishing SSL connection without server’s identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn’t set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to ‘false’. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification. 根据提示修改传入的 url 如下: 1jdbc:mysql://127.0.0.1:3306/jdbc-springboot?useSSL=false 警告消失了,这里就引入了第一个问题,MySQL 的连接安全,避免造轮子,我找到了网上写的一篇比较清楚的文章,公众号回复 SSL 查看。 反射12// 加载 mysql 数据库驱动Class.forName(\"com.mysql.jdbc.Driver\"); 上面的代码会加载 mysql 数据库驱动为什么?我们去看一下 forName 方法描述。 1234/*** A call to {@code forName(\"X\")} causes the class named* {@code X} to be initialized.*/ 方法被调用的时候,会去初始化我们传入的参数即”com.mysql.jdbc.Driver”,那我们再去看看 com.mysql.jdbc.Driver。 123456789101112public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException(\"Can't register driver!\"); } }} 看到上面的 Driver 类,我们可以看到一个静态代码块,这样初始化这个类的时候,这个代码块就会被执行。里面就一句代码,调用 DriverManager 注册驱动。","tags":[{"name":"JDBC","slug":"JDBC","permalink":"http://freemeng.com/tags/JDBC/"}]},{"title":"从JDBC到SpringBoot","date":"2022-03-08T01:28:11.000Z","path":"2022/03/08/从JDBC到SpringBoot/","text":"你没看错,就是这个主题“从 JDBC 到 SpringBoot”,这是个源码的学习记录,从 mysql-connector-java->commons-dbutils>mybatis->mybatis-spring>mybatis-spring-boot-starter。","tags":[{"name":"源码","slug":"源码","permalink":"http://freemeng.com/tags/源码/"}]},{"title":"Redis 使用规范学习笔记","date":"2022-02-16T07:35:07.000Z","path":"2022/02/16/Redis-使用规范学习笔记/","text":"为了更好的使用 Redis,学习一下其使用规范 键值对使用规范1、key 的命名规范使用业务名作为前缀,然后使用冒号分隔,再加上具体的业务数据名。 注意控制 key 的长度,如果 key 较长的话,会消耗较多内存空间。 举例: uv:page:1024 uv 代表业务 unique visitor(独立访客量) page 代表数据名称,网页 1024 代表具体的网页编号 2、避免使用 bigkey原因:Redis 使用单线程读写数据,bigkey 读写操作会阻塞线程,降低 Redis 的性能。 bigkey 情况一:键值对的值是 String 类型,本身比较大,例如 value 值为 1MB 的 String 数据类型。 建议:String 类型数据大小控制在 10 KB 以下。 bigkey 情况二:键值对的值是集合类型,集合元素个数非常多,例如包含 100 万个元素的 Hash 集合类型。 建议:集合类型数据,元素个数控制在 1 万以下。 3、使用高效序列化方法和压缩方法4、使用整数对象共享池Redis 内部维护了 0~9999 这一万个整数对象,并把这些整数作为一个共享池使用。 如果有一个键值对中使用了 0~9999 之间的一个整数,Redis 就不会为这个键值对专门创建整数对象了,而是会复用共享池中的整数对象。 注意有两种情况不能使用整数对象共享池。 第一种情况是,Redis 设置了 maxmemory,并且启用了 LRU 策略。 第二种情况是,集合类型数据采用 ziplist 编码。 数据保存规范1、使用 Redis 保存热数据2、不同业务数据分实例存储3、保存数据时要设置过期时间4、控制 Redis 实例的容量建议 2~6 GB 命令使用规范1、线上禁用部分命令keys、flushall、flushdb 禁用方法,通过 rename-command 命令,对上面三个命令进行重命名,让客户端无法使用这些命令。 2、慎用 monitor 命令3、慎用全量操作命令hgetall、smembers 替代方法:使用 sscan、hscan 命令,分批返回集合中数据 参考文档:《Redis 核心技术与实战》","tags":[{"name":"Redis","slug":"Redis","permalink":"http://freemeng.com/tags/Redis/"}]},{"title":"Kafka 认证配置实践笔记","date":"2021-08-29T07:37:57.000Z","path":"2021/08/29/Kafka-认证配置实践笔记/","text":"今天我们来实践 Kafka 安全配置,通过本篇教程,我们将配置一个需要使用用户名、密码访问的 Kafka。 本文使用 SASL/SCRAM 认证机制,是 Kafka 0.10.2 版本引入的。 测试环境: MacOs mojave,kafka_2.12-2.4.0 第一步,创建用户说明: admin 用于 broker 之间通信,writer 用于生产者,reader 用于消费者。 1234567bin/kafka-configs.sh --zookeeper localhost:2181 --alter --add-config 'SCRAM-SHA-256=[password=admin]' --entity-type users --entity-name adminbin/kafka-configs.sh --zookeeper localhost:2181 --alter --add-config 'SCRAM-SHA-256=[password=writer]' --entity-type users --entity-name writerbin/kafka-configs.sh --zookeeper localhost:2181 --alter --add-config 'SCRAM-SHA-256=[password=reader]' --entity-type users --entity-name reader 第二步,创建 JAAS 文件说明: 这个文件用于启动 broker,文件可以命名为 kafka-broker.jaas,broker 启动时需要指定该文件。 12345KafkaServer {org.apache.kafka.common.security.scram.ScramLoginModule requiredusername=\"admin\"password=\"admin\";}; 第三步,配置 server.properties 文件说明: 既然要进行认证,server.properties 文件是要进行配置的,参考下面的配置。 1234567sasl.enabled.mechanisms=SCRAM-SHA-256sasl.mechanism.inter.broker.protocol=SCRAM-SHA-256security.inter.broker.protocol=SASL_PLAINTEXTlisteners=SASL_PLAINTEXT://localhost:9092 第四步,启动 broker说明: 启动 broker 时,指定 jaas 文件,及修改后的 server.properties 文件。 1KAFKA_OPTS=-Djava.security.auth.login.config=<yourPath>/kafka-broker.jaas bin/kafka-server-start.sh config/server.properties 第五步,发送消息说明: 开启了认证后,我们的生产者,需要提供用户名、密码,因此需要配置一个文件,启动时指定,内容如下:可以命名为 producer.conf 123security.protocol=SASL_PLAINTEXTsasl.mechanism=SCRAM-SHA-256sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username=\"writer\" password=\"writer\"; 1bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test --producer.config <yourPath>/kafka_2.12-2.4.0/producer.conf 第六步,接收消息说明: 消费者和生产者一样,启动时,需要指定配置文件,配置文件的内容和生产者类似,只有用户名、密码不一致。内容如下:可以命名为 consumer.conf 123security.protocol=SASL_PLAINTEXTsasl.mechanism=SCRAM-SHA-256sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required username=\"reader\" password=\"reader\"; 1bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning --consumer.config <yourPath>/kafka_2.12-2.4.0/consumer.conf 总结我们配置了一个简单的 Kafka 认证,使用 SASL/SCRAM 认证机制。首先创建用户,设置密码,然后修改 server 配置文件,最后,producer 、consumer 在启动时,均需要提供用户名、密码。 这样一个简单的安全访问的 Kafka 就配置完成了。下篇继续介绍,如何进行授权。 参考文档及推荐阅读Kafka核心技术与实战 Apache Kafka","tags":[{"name":"Kafka","slug":"Kafka","permalink":"http://freemeng.com/tags/Kafka/"}]},{"title":"jasypt 配置文件加密实践笔记","date":"2021-08-22T02:22:59.000Z","path":"2021/08/22/jasypt-配置文件加密实践笔记/","text":"1.背景你知道你们生产环境数据库的用户名、密码么? 啥,你竟然知道?快来看看如何对其进行加密,防止密码泄漏。 2.Jasypt The easiest way to use Jasypt is using its easy encryption tools, which are called the utils, because they live in the org.jasypt.util package. They are called utils because they are ready-to-use, preconfigured digesters and encryptors you can use without knowing much about their configuration. 3.jasypt-spring-boot Jasypt Spring Boot provides Encryption support for property sources in Spring Boot Applications. 4.简单版-快速使用我们先来一个最简单版,体验一下效果。 1、添加 maven plugin 在我们要加密的配置文件所属的 module 的 pom.xml 中添加如下的 plugin 12345<plugin> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-maven-plugin</artifactId> <version>3.0.3</version> </plugin> 2、修改配置文件中的待加密的字符串 对数据库的用户名、密码使用 DEC() 进行包裹 12spring.datasource.username=DEC(root)spring.datasource.password=DEC(admin) 3、执行 maven plugin 插件命令,对配置文件加密 切换到配置 plugin 的 module 目录下,执行下面的命令 1mvn jasypt:encrypt -Djasypt.encryptor.password=\"the password\" 成功后,我们可以看到,配置文件待加密项,变成了如下的样子,这次别人拿到你的代码,也不知道用户名、密码是什么了,大功告成。 12spring.datasource.username=ENC(/xSAK8u53npb+F+hH+DpvcVzE0qm5ubh5BhLgqyWK1mFzSz1MVzKvu/NlEKGl/Iy)spring.datasource.password=ENC(BHUN3TvKMaerfSOiFfCrxyGHAyYxB3qOzstn/eqaXA54WlYCairCTAjrY+68TA4w) 4、配置解密用的密码,启动项目 配置文件这个样子了,项目还能启动么?当然可以,但是我们要指定一下,密码,对就是在执行 maven plugin 时传入的那个。 1--jasypt.encryptor.password=\"the password\" 我是用 idea 测试,所以将参数填写在,Program arguablyments 内,如下图: 是不是很简单,想简单测试一下,源码地址在下面。 5、高级版-自定义加密算法有的时候,默认的加密算法安全等级不合适,这个时候我们需要自定义加密算法,来,搞起来! 1、实现 StringEncryptor 接口加解密方法 注意添加注解 @Component,示例代码中的加密、解密为了演示效果,使用的是一个简单字符串替换。 12345678910111213141516171819202122232425@Componentpublic class MyStringEncryptor implements StringEncryptor { @Override public String encrypt(String s) { switch (s) { case \"root\": return \"root-en\"; case \"admin\": return \"admin-en\"; } return null; } @Override public String decrypt(String s) { switch (s) { case \"root-en\": return \"root\"; case \"admin-en\": return \"admin\"; } return null; }} 2、修改配置文件,手动配置加密后的字符串 这个时候,不能使用插件命令去加密了,我们需要自己手动去加密,项目只负责解密 12spring.datasource.username=ENC(root-en)spring.datasource.password=ENC(admin-en) 3、指定我们的加解密类 1jasypt.encryptor.bean=myStringEncryptor 4、启动项目 1--jasypt.encryptor.password=\"the password\" 6、实践代码代码已经上传至 github,https://github.com/zmdstr/jasypt-demo 7、参考文章与推荐阅读jasypt jasypt-spring-boot","tags":[{"name":"安全","slug":"安全","permalink":"http://freemeng.com/tags/安全/"}]},{"title":"Arthas 进阶体验二","date":"2021-01-23T13:32:47.000Z","path":"2021/01/23/Arthas-进阶体验二/","text":"本文为 Arthas 进阶体验记录 watch 查看 UserController 的 参数/异常 1watch com.example.demo.arthas.user.UserController * '{params, throwExp}' 'params[0] > 100' -x 2 第一个参数是类名,支持通配 第二个参数是函数名,支持通配 第三个参数是返回值表达式,它实际上是一个ognl表达式 第四个参数里写条件表达式 如果想把获取到的结果展开,可以用-x参数: 当异常时捕获 -e选项,表示只捕获抛出异常时的请求 1watch com.example.demo.arthas.user.UserController * \"{params[0],throwExp}\" -e 按照耗时进行过滤 1watch com.example.demo.arthas.user.UserController * '{params, returnObj}' '#cost>200' 热更新代码通过jad/mc/redefine 命令实现动态更新代码的功能。 1jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java 1sc -d *UserController | grep classLoaderHash 1mc -c 1be6f5c3 /tmp/UserController.java -d /tmp 1redefine /tmp/com/example/demo/arthas/user/UserController.class","tags":[{"name":"Arthas","slug":"Arthas","permalink":"http://freemeng.com/tags/Arthas/"}]},{"title":"Arthas 进阶体验","date":"2021-01-23T10:03:27.000Z","path":"2021/01/23/Arthas-进阶体验一/","text":"本文为 Arthas 进阶体验记录 查看 JVM 信息 sysprop sysprop 可以打印所有的 System Properties 信息。 也可以指定单个 key: sysprop java.version。 也可以通过grep来过滤: sysprop | grep user。 可以设置新的value: sysprop testKey testValue。 sysenvsysenv 命令可以获取到环境变量。和sysprop命令类似。 jvm jvm 命令会打印出JVM的各种详细信息。 Tips help 可以用-h来查看帮助。 1sysprop -h 自动补全 readline 的快捷键支持Ctrl + A跳转行首,Ctrl + E跳转行尾。 更多的快捷键可以用 keymap 命令查看。 pipeline sc/sm 查看已加载的类 sc sc 命令可以查找到所有 JVM 已经加载到的类。 如果搜索的是接口,还会搜索所有的实现类。比如查看所有的Filter实现类: 通过-d参数,可以打印出类加载的具体信息,很方便查找类加载问题。 sc支持通配。 sm sm命令则是查找类的具体函数。 通过-d参数可以打印函数的具体属性。 也可以查找特定的函数,比如查找构造函数: Ognl在 Arthas 里,有一个单独的ognl命令,可以动态执行代码。 调用 static 函数 1ognl '@java.lang.System@out.println(\"hello ognl\")' 获取静态类的静态字段 1ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader @com.example.demo.arthas.user.UserController@logger 在 Arthas 里ognl表达式是很重要的功能,在很多命令里都可以使用ognl表达式。 OGNL特殊用法请参考:https://github.com/alibaba/arthas/issues/71 OGNL表达式官方指南:https://commons.apache.org/proper/commons-ognl/language-guide.html","tags":[{"name":"Arthas","slug":"Arthas","permalink":"http://freemeng.com/tags/Arthas/"}]},{"title":"Arthas 初体验","date":"2021-01-23T09:29:13.000Z","path":"2021/01/23/Arthas-初体验/","text":"本文主要记录 arthas 的一些功能,体验一下 arthas 的强大。 Arthas 是 Alibaba 开源的 Java 诊断工具。在线排查问题,无需重启;动态跟踪 Java 代码;实时监控 JVM 状态。 Github: https://github.com/alibaba/arthas 文档: https://arthas.aliyun.com/doc/ dashboarddashboard 命令可以查看当前系统的实时数据面板。 Threadthread 1 命令会打印线程 ID 1 的栈。 支持管道,可以用 thread 1 | grep 'main(' 查找到main class。 查看 CPU使用率 top n 线程的栈 1thread -n 3 查看 5 秒内的 CPU 使用率 top n 线程栈 1thread -n 3 -i 5000 查找线程是否有阻塞 1thread -b SC可以通过 sc 命令来查找 JVM 里已加载的类: jad可以通过 jad 命令来反编译代码。 watch通过watch命令可以查看函数的参数/返回值/异常信息。 Exit/Stop用 exit 或者 quit 命令可以退出 Arthas。 exit/quit命令只是退出当前 session,arthas server 还在目标进程中运行。 Arthas 在 watch/trace 等命令时,实际上是修改了应用的字节码,插入增强的代码。显式执行 reset 命令,可以清除掉这些增强代码。 想完全退出 Arthas,可以执行 stop 命令。 总结以上内容,来源 Arthas 基础教程,可以看到从系统监控、到线程、JVM 中类、反编译、方法监控,Arthas 游刃有余,非常值得尝试和学习,并纳入自己的工具库。","tags":[{"name":"Arthas","slug":"Arthas","permalink":"http://freemeng.com/tags/Arthas/"}]},{"title":"工具汇总篇","date":"2021-01-23T09:16:45.000Z","path":"2021/01/23/工具汇总篇/","text":"ArthasArthas 是 Alibaba 开源的 Java 诊断工具。在线排查问题,无需重启;动态跟踪 Java 代码;实时监控 JVM 状态。 Github: https://github.com/alibaba/arthas 文档: https://arthas.aliyun.com/doc/","tags":[{"name":"Tool","slug":"Tool","permalink":"http://freemeng.com/tags/Tool/"}]},{"title":"分布式链路追踪的那些事","date":"2021-01-10T08:25:50.000Z","path":"2021/01/10/分布式链路追踪的那些事/","text":"什么是分布式链路追踪现代互联网服务,都是由复杂、大规模的分布式系统实现的。用户的一个请求,需要多个服务配合完成。 举个例子: 周末,你网购了一单水果,发送了一个创建订单的请求,这时可能就会涉及订单服务、库存服务、统计服务等多个服务。 这些服务有的是并行,有的是串行。如何知道每个服务耗时多久呢? 如果是单体服务,我们找到请求的线程,限定时间,根据线程 ID 在各个服务有日志的情况下,我们是可以查到的。可现在呢? 我们面对的是多个服务,涉及多台服务器,如何确定这次请求的情况呢? 这时,我们就需要分布式链路追踪系统了。 分布式链路追踪,是分布式系统的基础设施之一。它帮助我们确定一次请求的链路,理清分布式系统服务间的关系。 可以简单理解为高级版日志。 有哪些实现常见的有 Twitter 开源的 Zipkin 和 国产的 Skywalking。 目前,Skywalking 以低损耗、零侵入,备受欢迎。","tags":[{"name":"分布式","slug":"分布式","permalink":"http://freemeng.com/tags/分布式/"}]},{"title":"Hash 学习笔记","date":"2020-11-08T08:54:54.000Z","path":"2020/11/08/Hash-学习笔记/","text":"1、什么是 HashHash 又称散列,就是把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出就是散列值。 压缩映射 举例:MD5 和 SHA 都是历史悠久的 Hash 算法。 2、Hash 算法的用途1)安全加密2)唯一标识3)数据校验4)散列函数5)负载均衡6)数据分片7)分布式存储 3、Hash 的特点1)从 Hash 值不可以反向推导出原始值;2)输入数据的微小变化,会得到完全不通的 Hash 值;3)Hash 算法的执行效率要高效,对长文本也能快速计算出 Hash 值;4)Hash 算法的冲突概率要小。 以上 4 个特点,也是衡量一个 Hash 算法好坏的标准,需要根据应用场景进行取舍,选择合适 Hash 算法。 4、Hash 碰撞因为 Hash 算法是压缩映射,即输入值空间远大于 Hash 值空间,根据数学抽屉原理,一定存在两个不同的输入,经过 Hash 等到相同的 Hash 值,这时就是发生了 Hash 碰撞。 5、如何防止 Hash 碰撞最有效的方法就是扩大 Hash 值空间。 16 个二进制位 Hash 值,产生 Hash 碰撞的可能性是 65536 分之一。 32 个二进制位 Hash 值,产生 Hash 碰撞的机会是 4294967296 分之一。 我们熟悉的 MD5 生成的是 128 个二进制位的 Hash 值,其发生 Hash 碰撞的可能性就更低了。 更长的 Hash 值,意味着需要更大的存储空间,更高的运算成本,应根据实际使用场景进行取舍。 6、Hash 碰撞的解决方案什么时候需要解决 Hash 碰撞呢?Java 中的 HashMap 数据结构,作为一个容器类,目的是存储数据,发生碰撞时,是需要解决的。 比较常用的算法有链表法和开放寻址法。 7、一致性 Hash 算法在分布式存储应用中,会根据数据进行计算 Hash 值,然后和机器总数取余数,确定要存储的机器,如果刚开始是 3 台机器,当机器增加到 4 台时,这时就需要对所有数据进行重新 Hash,来确定新的存储位置,成本很高。 一致性 Hash 算法,将 Hash 值的范围划分为几个区,每台机器负责几个区,这样在新增机器时,仅需要对几个区进行重新计算 Hash 和数据搬移了。 8、参考文档1、《网易云课堂》2、《极客时间-数据结构与算法》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"Docker 知识梳理","date":"2020-09-13T01:59:19.000Z","path":"2020/09/13/Docker-知识梳理/","text":"Docker 知识图谱","tags":[{"name":"Docker","slug":"Docker","permalink":"http://freemeng.com/tags/Docker/"}]},{"title":"设计模式-初篇","date":"2020-07-26T08:53:28.000Z","path":"2020/07/26/设计模式-初篇/","text":"记得四年前买了一本书《大话设计模式》,便开始了设计模式的学习。因为每天到公司比较早,便开始阅读这本书,学习设计模式。当时做 ToC 电商,业务刚刚起步,规模不大,业务逻辑不是很复杂,可想而知,意识不到设计模式的使用,仅仅每天看书,学习效果一般。 随着开发经验增长,接触的业务逻辑越来越复杂,越发意识到代码的可拓展性、可维护性的重要。因此,准备重温一下设计模式。 C 语言是大学必修课,它是面向过程编程语言,也许那时候我们就习惯并接受了面向过程编程的思想。一个功能,写一个函数,然后调用。 对于 Java 开发者,我们的银弹是面向对象,思维必须转变,不然,代码可维护性差、扩展性差。当需求不断变更时,等待你的就是无休止的加班。 设计原则Java 程序员 平时开发时,考虑过设计原则么? 代码 review 时,考虑过设计原则么? 记住这六个原则,在新建一个类时,在修改一个类时,衡量一下,这么做是否符合设计原则,是在利用面向对象的思维开发么?代码是可扩展的么? 单一职责就一个类而言,应该仅有一个引起它变化的原因。 开闭原则对于扩展是开放的,对于更改是封闭的。 里氏替换原则子类型必须能够替换掉它们的父类型。 依赖倒置原则抽象不应该依赖细节,细节应该依赖于抽象。针对接口编程,不要对实现编程。 接口隔离原则保持接口粒度。 迪米特法则最小知识原则。如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法,可以通过第三者转发这个调用。 设计模式设计模式是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。大致分为三个类型,创建型模式、结构型模式、行为型模式。 后面将从 JDK 源码、Spring 源码去学习设计模式,希望这样可以成功掌握银弹。","tags":[{"name":"设计模式","slug":"设计模式","permalink":"http://freemeng.com/tags/设计模式/"}]},{"title":"ZooKeeper 服务搭建伪集群版","date":"2020-05-02T02:52:42.000Z","path":"2020/05/02/ZooKeeper-服务搭建伪集群版/","text":"以下操作均为 macOS 下进行,搭建伪集群,在一台机器上,使用不同端口号 1、下载之前,我们搭建了单机版,现在开始搭建集群版,第一步还是下载。 官网下载地址 2、安装无需安装,解压后进行配置即可。 解压 1tar -zxvf apache-zookeeper-3.6.0.tar.gz 配置文件修改进入 conf 目录,复制 zoo_sample.cfg 为 zoo1.cfg。 1cp zoo_sample.cfg zoo1.cfg 简单看一下配置:12345tickTime=2000initLimit=10syncLimit=5dataDir=/tmp/zookeeperclientPort=2181 这里有两个,与集群相关的参数,简单解释一下。 initLimit:集群中 follower 服务器和 leader 服务器,在完成初始化连接过程中,所能容忍的最大心跳数。 syncLimit:集群中 folloer 服务器和 leader 服务器,在请求和应答中,所能容忍的最大心跳数。 搭建一个由三个服务构成的 ZooKeeper 伪集群,我们需要启动三个 ZooKeeper 服务,因此需要三个配置文件,依次伪 zoo_1.cfg zoo_2.cfg zoo_3.cfg。三个配置文件内容如下:12345678tickTime=2000initLimit=10syncLimit=5dataDir=/tmp/zookeeper1clientPort=2191server.1=127.0.0.1:2881:3881server.2=127.0.0.1:2882:3882server.3=127.0.0.1:2883:3883 12345678tickTime=2000initLimit=10syncLimit=5dataDir=/tmp/zookeeper2clientPort=2192server.1=127.0.0.1:2881:3881server.2=127.0.0.1:2882:3882server.3=127.0.0.1:2883:3883 12345678tickTime=2000initLimit=10syncLimit=5dataDir=/tmp/zookeeper3clientPort=2193server.1=127.0.0.1:2881:3881server.2=127.0.0.1:2882:3882server.3=127.0.0.1:2883:3883 注意,由于在一台机器上搭建,因此我们对 dataDir、clientPort 进行了区别设置,如果在三台机器上,就不需要这样额外处理。 创建 myid进入 dataDir 目录,创建 zookeeper1、zookeeper2、zookeeper3,并依次进入对应目录中,创建文件 myid,内容为 1、2、3。举例:1234cd /tmpmkdir zookeeper1cd zookeeper1echo \"1\" >> myid 3、启动123bin/zkServer.sh start conf/zoo_1.cfgbin/zkServer.sh start conf/zoo_2.cfgbin/zkServer.sh start conf/zoo_3.cfg 4、验证 5、总结ZooKeeper 伪集群搭建较容易,注意配置文件修改和 myid 设置即可。 6、参考文献《网易公开课》","tags":[{"name":"ZooKeeper","slug":"ZooKeeper","permalink":"http://freemeng.com/tags/ZooKeeper/"}]},{"title":"ZooKeeper 核心概念","date":"2020-05-02T02:52:27.000Z","path":"2020/05/02/ZooKeeper-核心概念/","text":"主要介绍 Session 会话、数据模型、watch 监听机制、ZooKeeper 特性和典型应用场景。 Session 会话 一个客户端连接一个会话,由服务端分配唯一的会话 id 客户端以特定的时间间隔(TickTime),向服务端发送心跳,以保持会话有效 超过会话超时时间而未收到客户端心跳,则判定客户端挂掉 会话中的请求按 FIFO 顺序执行 数据模型 层次命名空间类似 Unix 文件系统,区别:节点可以包含与之相关的数据和子节点(即是文件夹也是文件) znode四种类型:持久、顺序、临时、临时顺序 数据构成节点数据和节点元数据 数据量上限1M watch 监听机制客户端可以在 znode 上,设置 watch ,监听节点变化。 watch 类型1、data watch 监听数据变化2、child watch 监听子节点变化 watch 特性1、一次性触发2、有序性 ZooKeeper 特性 顺序一致性保证客户端操作,是按顺序生效的 可靠性数据的变更不会丢失 原子性更新成功或失败,没有部分结果 及时性保证客户端读取到的信息是最新的 单系统镜像无论连接到哪个服务器,都看到相同的配置 典型应用场景 配置中心 服务发现 Master 选举 分布式队列 分布式锁 总结主要介绍了 ZooKeeper 的 session 会话、数据模型、watch 监听机制、特性及典型应用场景。 参考文档《网易云课堂》","tags":[{"name":"ZooKeeper","slug":"ZooKeeper","permalink":"http://freemeng.com/tags/ZooKeeper/"}]},{"title":"ZooKeeper 服务搭建单机版","date":"2020-05-02T02:52:11.000Z","path":"2020/05/02/ZooKeeper-服务搭建单机版/","text":"说明:以下操作,均在 macOS 系统下进行 1、下载 ZooKeeper官网下载地址 目前最新版本为 3.6.0,点击 Apache ZooKeeper 3.6.0 (asc, sha512) 下载。 12345678910111213141516171819202122 apache-zookeeper-[version].tar.gz Contains all the source files which can be built by running: mvn clean install To generate an aggregated apidocs for zookeeper-server and zookeeper-jute: mvn javadoc:aggregate (generated files will be at target/site/apidocs) apache-zookeeper-[version]-bin.tar.gz Contains all the jar files required to run ZooKeeper Full documentation can also be found in the docs folder``` #### 2、安装无需安装,解压后进行配置即可。- 解压```shelltar -zxvf apache-zookeeper-3.6.0.tar.gz 配置 进入 conf 目录,复制 zoo_sample.cfg 为 zoo.cfg 1cp zoo_sample.cfg zoo.cfg 简单看一下配置:12345tickTime=2000initLimit=10syncLimit=5dataDir=/tmp/zookeeperclientPort=2181 配置暂时,全部使用默认,不做任何调整。 dataDir:事务日志和快照日志的存储位置。 启动12345cd apache-zookeeper-3.6.0-bin# 方式一bin/zkServer.sh start# 方式二bin/zkServer.sh start conf/zoo.cfg 停止1bin/zkServer.sh stop 验证1jcmd 使用客服端连接服务端进行验证 1bin/zkCli.sh -server 127.0.0.1:2181 总结主要介绍了,单机版 ZooKeeper 的安装及验证,内容比较简单。ZooKeeper 的安装,很简单,下载后,简单复制一份配置文件即可使用。 参考文档Difference between Binary release and source release? Welcome to Apache ZooKeeper™ 14.1 zookeeper日志查看","tags":[{"name":"ZooKeeper","slug":"ZooKeeper","permalink":"http://freemeng.com/tags/ZooKeeper/"}]},{"title":"ZooKeeper 简介","date":"2020-05-02T02:51:50.000Z","path":"2020/05/02/ZooKeeper-简介/","text":"简介ZooKeeper 是分布式协调服务 集中式信息存储 分布式协调服务一个不知名的小网站,初期用户访问量比较低,一台服务器即满足了需求,这时是一个单机架构。 随着互联网的兴起,网站知名度提高,用户量激增,一台服务器,已经无法应对大量的请求。应用服务器变成了多台,业务进行了拆分,形成了分布式架构。 以前,一台服务器,资源的协调在一个进程中,使用锁完成,现在,多台服务器,资源的协调需要新的工具,即分布式协调。例如:分布式锁。 将分布式系统中,负责分布式服务协调的公共部分抽取出来,就构成了一个分布式协调服务。 数据模型ZooKeeper 数据模型类似文件系统,不同之处是,非叶子节点同样可以存储数据。 简单操作使用客户端连接服务端后,输入 help,即可看到帮助文档,介绍了各种命令使用方式。 1help ZooKeeper 应用案例第一次接触 ZooKeeper,是作为 Dubbo 的注册中心,第二次是作为 Kafka 的依赖。 Hbase 使用 ZooKeeper 进行 Master 选举,服务间协调。 ShardingSphere 使用 ZooKeeper 进行集群管理、配置管理。 总结ZooKeeper 是分布式协调服务,数据模型类似文件系统,被大量应用于分布式系统中,充当 master 选举、服务协调、配置管理等角色,是分布式系统中的重要组成部分。 参考文档《网易公开课》","tags":[{"name":"ZooKeeper","slug":"ZooKeeper","permalink":"http://freemeng.com/tags/ZooKeeper/"}]},{"title":"docker 实践","date":"2020-03-28T11:41:56.000Z","path":"2020/03/28/docker-实践/","text":"之前学习了 Docker,但是一直没实际去使用它,平时使用 Zookeeper、ES 等,都是在 mac OS 下载直接使用的,本着学以致用的态度,近期准备将平时使用的服务,全部迁移到 Docker。 今天就从 ZooKeeper 开始,毕竟使用 Dubbo,Kafka 都可能需要依赖一个 ZooKeeper。如果只是启用一个 ZooKeeper Docker 容器,那太简单了,本文最终将编排一个项目,包括两个服务:一个 ZooKeeper,一个 zkui(ZooKeeper 可视化工具)。 内容主要为实践,理论较少,需要一定 Docker 基础。 Dockerfile 学习如何让我们的项目,以 Docker 镜像的方式运行?答案是编写 Dockerfile。👌,让我们去看看 zkui 的 Dockerfile,项目地址:https://github.com/DeemOpen/zkui/ 进入 到 docker 目录,就可以看到 Dockerfile 文件,其内容为: 12345678910111213FROM java:8MAINTAINER Miguel Garcia Puyol <miguelpuyol@gmail.com>WORKDIR /var/appADD zkui-*.jar /var/app/zkui.jarADD config.cfg /var/app/config.cfgADD bootstrap.sh /var/app/bootstrap.shENTRYPOINT [\"/var/app/bootstrap.sh\"]EXPOSE 9090 简单解释一下 FROM 指定基础镜像 MAINTAINER 维护者 WORKDIR 指定工作目录 ADD 更高级的复制文件,建议使用 COPY 替代 ENTRYPOINT 入口点,指定启动容器时,所运行的程序 EXPOSE 申明端口 总结一下这个 Dockerfile 的内容,基于基础镜像 java 8 ,往工作目录 /var/app 复制了,一个 jar 包,一个配置文件,一个启动脚本,最后声明端口 9090。下面,我们再看看那个启动脚本。 启动脚本123456789101112131415#!/bin/shZK_SERVER=${ZK_SERVER:-\"localhost:2181\"}USER_SET=${USER_SET:-\"{\\\"users\\\": [{ \\\"username\\\":\\\"admin\\\" , \\\"password\\\":\\\"manager\\\",\\\"role\\\": \\\"ADMIN\\\" \\},{ \\\"username\\\":\\\"appconfig\\\" , \\\"password\\\":\\\"appconfig\\\",\\\"role\\\": \\\"USER\\\" \\}]\\}\"}LOGIN_MESSAGE=${LOGIN_MESSAGE:-\"Please login using admin/manager or appconfig/appconfig.\"}sed -i \"s/^zkServer=.*$/zkServer=$ZK_SERVER/\" /var/app/config.cfgsed -i \"s/^userSet = .*$/userSet = $USER_SET/\" /var/app/config.cfgsed -i \"s|^loginMessage=.*$|loginMessage=$LOGIN_MESSAGE|\" /var/app/config.cfgecho \"Starting zkui with server $ZK_SERVER\"exec java -jar /var/app/zkui.jar 这个脚本是修改后的,原项目的 sed 运行时报错,我做了简单修改,上面的是修改后的。 构建镜像进入到 Dockerfile 同级目录,执行 1docker build -t zkui:2.0-SNAPSHOT . 说明:当前目录,没有 config.cfg 和 jar(zkui-2.0-SNAPSHOT-jar-with-dependencies.jar),我复制了一份放在当前目录后,构建成功。 查看镜像 1docker image ls 如果看到我们刚刚构建的 zkui,那么构建镜像就成功了。 接下来我们要编写 compose.yml 文件,构建一个项目,包括两个服务,一个 ZooKeeper ,一个 zkui。这样启动后 Zookeeper 有了,其图形化 UI 也有了,还要啥。 编写 compose.yml12345678910111213141516171819202122version: '3.4'services: zoo: image: zookeeper container_name: zookeeper# restart: always ports: - 2181:2181 volumes: - ~/docker/zookeeper/data:/data - ~/data/zookeeper/datalog:/datalog zkui: image: zkui:2.0-SNAPSHOT container_name: zkui ports: - 9090:9090 depends_on: - zoo links: - zoo environment: ZK_SERVER: zoo:2181 compose.yml 就不介绍了,比较简单。接下来我们启动项目: 1docker-compose up 然后查看一下容器: 1docker container ls 大功告成!以后 Zookeeper 就用这个 Docker Container 了。","tags":[{"name":"Docker","slug":"Docker","permalink":"http://freemeng.com/tags/Docker/"}]},{"title":"「第34讲」有人说“Lambda能让Java程序慢30倍”,你怎么看?|学习笔记","date":"2020-01-05T14:37:03.000Z","path":"2020/01/05/「第34讲」有人说“Lambda能让Java程序慢30倍”,你怎么看?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 34 篇学习笔记,坚持写完 36 讲学习笔记。 A:“Lambda 能让 Java 程序慢 30 倍”B:“真假,你看不懂我写的 Lambda,才这样说的吧。还 30 倍,你测试过?” 如何从代码级别,判断应用的性能表现,这一讲主要介绍基准测试(Benchmark)。这个词大家应该有印象,中间件、数据库等,涉及多个组件进行比较时,都会有这样一环节,就像手机硬件跑分,大家在环境一致的前提下,一起比一比,看谁的性能好。 一、微基准测试(Micro-Benchmark)工具:JMH JMH is a Java harness for building, running, and analysing nano/micro/milli/macro benchmarks written in Java and other languages targetting the JVM. 如何进行微基准测试参考官网指南没事多读读英文 微基准测试有哪些坑如何保证测试的正确性?有哪些坑需要避免?我们进行测试时,要重点关注 JVM,因为它会优化我们的代码。1、保证代码经过了足够且合适的预热。使用下面参数,判断预热到底经过了多久?1-XX:+PrintCompilation 2、关闭后台编译1-Xbatch 3、防止 JVM 进行无效代码消除下面的代码由于,我们最终没有使用 mul,JVM 就可能直接判定为无效代码,不执行它。123456public void testMethod() {int left = 10;int right = 100;int mul = left * right;} 解决办法,使用 JMH 提供的 BlackHole 设施1234public void testMethod(Blackhole blackhole) {// …blackhole.consume(mul);} 4、GCSerial GC 是个值得考虑的选项,或者 JDK 11 Epsilon GC 什么也不做的 GC,从最大可能性去排除相关影响。 二、Lambda 局限性1、初始化开销2、不利于调试,不利于异常处理 三、总结微基准测试,是一个需要高度了解 Java、JVM 底层机制的技术,是个非常好的深入理解程序背后效果的工具。 四、参考文档《Java 核心技术 36 讲》JMH","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「查漏补缺」多线程01|面试","date":"2019-10-23T11:11:23.000Z","path":"2019/10/23/「查漏补缺」多线程01-面试/","text":"编写正确的程序很难,编写正确的并发程序则难上加难! 线程什么时候会让出 CPU ? 阻塞时 wait await 等待 IO Sleep yield 结束了 线程为什么不是越多越好 ? 每一个 Java 线程,都需要一个操作系统线程支持,线程创建、销毁需要时间 新建一个线程,需要在堆内,新建一个对象,占用堆内存,与之对应的系统线程,需要占用系统内存,线程过多,会占用很多内存资源 操作系统会频繁切换线程上下文,消耗性能 如何确定合适数量的线程 ? 1.计算型任务 CPU 数量的 1~2 倍 2.IO 型任务 需要根据 IO 阻塞时间具体考虑 一个线程如果出现了运行时异常会怎么样? 如果这个异常没有被捕获的话,这个线程就停止了 如果这个线程持有某个对象的监视器,那么这个监视器会被立即释放 sleep 方法和 wait 方法有什么区别? sleep 方法和 wait 方法都可以用来放弃一段 CPU 时间,不同的地方在于,如果线程持有某个对象的监视器,sleep 方法不会释放这个对象的监视器,而 wait 会释放这个对象的监视器,因此 wait 被用于线程间通信。","tags":[{"name":"多线程","slug":"多线程","permalink":"http://freemeng.com/tags/多线程/"}]},{"title":"「第33讲」后台服务出现明显变慢,谈谈你的诊断思路|学习笔记","date":"2019-08-11T08:02:16.000Z","path":"2019/08/11/「第33讲」后台服务出现明显变慢,谈谈你的诊断思路-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 33 篇学习笔记,坚持写完 36 讲学习笔记。 一、后台服务出现明显变慢,谈谈你的诊断思路在正面回答之前,先探讨更加精确的问题定义是什么? 1、“变慢”是指响应时间变长么?2、变慢是突然出现的?还是周期性出现?3、是所有接口都变慢了么? 只有弄清楚问题的定义,我们才能避免答非所问。 思路:1、确定是否是应用内部通过日志确认,是自己的应用慢还是因为调用其他系统慢。2、是否出现程序错误检查应用日志,是否出现异常。3、检查系统级别资源检查系统 CPU、内存等资源是否被其他进程大量占用,并且这种占用是不合理的。4、检查 GC是否有 Full GC 等恶劣情况出现,或者 Minor GC 在变长。5、对应用进行 profiling对系统产生侵入性,大多数情况不建议在生产系统进行。 二、系统性能分析系统分析中,CPU、内存和 IO 是主要关注项,对应命令。 top free iostat 三、JVM 分析 利用(Java Mission Control)JMC、JConsole 等工具进行运行时监控 堆转存分析 GC 日志 Profiling使用 JFR 配合 JMC 来做 profiling。其性能开销低于 2 %。它的使用非常方便,不需要重启应用,或者提前配置。你可以在运行时启动 JFR 记录,并将这段时间的信息写入文件。12jcmd <pid> VM.unlock_commercial_featuresJcmd <pid> JFR.start duration=120s filename=myrecording.jfr 然后使用,JMC 打开“.jfr文件”就可以分析了,方法、异常、线程、IO 等应有尽有,其功能非常强大。 画外音:我在 mac 上尝试打开了几次 JMC ,嗯,好卡,一直使用不了。 四、 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第32讲」如何写出安全的Java代码?|学习笔记","date":"2019-07-06T08:53:09.000Z","path":"2019/07/06/「第32讲」如何写出安全的Java代码?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 32 篇学习笔记,坚持写完 36 讲学习笔记。 1. 拒绝服务(DoS)攻击DoS 是一种常见的网络攻击,有人称其为“洪水攻击”。最常见的表现是,利用大量机器发送请求,将目标网站的带宽或者其他资源耗尽,导致其无法响应正常用户的请求。 2. 写出安全的 Java 代码所必须考虑的事情 类似加密、解密、图形处理等计算密集型任务,都要防范恶意滥用,以免攻击者通过直接调用或者间接触发方式,消耗系统资源。 利用 Java 构建类似上传文件或者其他接受输入的服务,需要对消耗系统的内存或者存储的上限有所控制,因为我们不能将系统安全依赖于用户的合理使用。 任何情况下都应该保证资源释放成功,否则即使平时能够正常运行,也可能被攻击者利用而耗尽某类资源,这也算是可能的 DoS 攻击。 3. Java 反序列化安全问题1、敏感信息不要被序列化;2、反序列化中,建议在 readObject 中实现与对象构件过程相同的安全检查和数据检查。 4. 不同阶段,有哪些常见的安全策略或工具 开发和测试阶段1、尽量使用广泛验证过的工具、类库;2、开发过程中应用代码规约标准;3、利用多种静态分析工具如 FindBugs;4、重视警告。 部署阶段1、注意升级 JDK,修补已知漏洞;2、如果是安全敏感型产品,建议关注 JDK 在加解密方面的路线图。 5. 参考文档《Java 核心技术 36 讲》java反序列化漏洞原理研习","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第31讲」你了解java应用开发中的注入攻击吗?|学习笔记","date":"2019-06-29T08:29:33.000Z","path":"2019/06/29/「第31讲」你了解java应用开发中的注入攻击吗?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 31 篇学习笔记,坚持写完 36 讲学习笔记。 1. 注入式攻击注入式(Inject)攻击是一类非常常见的攻击方式,其基本特征是程序允许攻击者将不可信的动态内容注入到程序中,并将其执行,这就可能完全改变最初预计的执行过程,产生恶意效果。1)SQL 注入攻击2)操作系统命令注入3)XML 注入攻击 2.Java 安全基础第一,运行时安全机制。第二,Java 提供的安全框架 API,这是构建安全通信等应用的基础。第三,JDK 集成的各种安全工具 3. 安全漏洞任何可以用来绕过系统安全策略限制的程序瑕疵,都可以算作安全漏洞。 4. SQL 注入解决方式 在数据输入阶段,进行输入校验,限定什么类型的输入是合法的。 在 Java 应用访问数据库时,不用完全的动态 SQL,而是利用 PreparedStatement 可以有效防范 SQL 注入。 在数据库层面,做好账号的权限限定。 5. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第30讲」Java 程序运行在 Docker 等容器环境有什么新问题?|学习笔记","date":"2019-06-23T11:03:56.000Z","path":"2019/06/23/「第30讲」Java-程序运行在-Docker-等容器环境有什么新问题?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 30 篇学习笔记,坚持写完 36 讲学习笔记。 1. Java 程序运行在 Docker 等容器环境有什么新问题?1.基础问题Docker 和虚拟机不同,只做了轻量级的隔离,因此早期版本的 Java 就无法识别,Docker 对内存、CPU 的限制,在使用默认设置时,由于识别到错误的内存、CPU 限制,导致使用了超出容器限制的内存,及不合理的线程数。最终被容器杀掉。2.应用打包、发布角度JDK 自身就比较大,生成的镜像更为臃肿,当我们的镜像非常多的时候,镜像的存储等开销就比较明显了。3.新架构、场景方面考虑到微服务、Serverless 等新的架构场景,Java 自身的大小、内存占用、启动速度,都存在一定局限性,因为 Java 早期的优化大多针对长时间运行的大型服务器端应用。 2. Docker 到底有什么特别?Docker 和虚拟机不同。Docker 并不是一种完全的虚拟化技术,而更是一种轻量级的隔离技术。Docker 仅在类似 Linux 内核之上实现了有限的隔离和虚拟化,并不是像传统虚拟化软件那样,独立运行一个新的操作系统。它的限制对于应用是不透明的,需要用户理解 Docker 的新行为。 3. 从 JVM 运行机制的角度,为什么这些“沟通障碍”会导致 OOM 等问题呢?这个问题实际是反映了 JVM 如何根据系统资源(内存、CPU等)情况,在启动时设置默认参数。这就是所谓的 Ergonomics[ˌɜːrɡəˈnɑːmɪks] 机制。例如:1、JVM 会大概根据检测到的内存大小,设置最初启动时的堆大小为系统内存的 1/64;并将堆最大值,设置为系统内存的 1/4。2、而 JVM 检测到的系统的 CPU 核数,则直接影响到了 Parallel GC 的并行线程数目和 JIT complier 线程数目,甚至是我们应用中 ForkJoinPool 等机制的并行等级。这些默认值,是根据通用场景选择的初始值。但由于 Docker 的影响,JVM 的判断是基于错误信息而做出来的。这就类似,我以为我住的是整栋别墅,实际上却只有一个房间是给我住的。 4. 如何解决这些问题1、升级到最新的 JDK 版本,一切问题就都解了。2、无法升级,建议1)明确设置堆、元数据区等内存区域大小,保证 Java 进程的总大小可控。2)明确配置 GC 和 JIT 并行线程数目,以避免二者占用过多计算资源。 5. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第29讲」Java内存模型中的 happen-before 是什么?|学习笔记","date":"2019-06-08T09:17:04.000Z","path":"2019/06/08/「第29讲」Java内存模型中的-happen-before-是什么?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 29 篇学习笔记,坚持写完 36 讲学习笔记。 1. 为什么需要 JMM,它试图解决什么问题?JMM(Java Memory Model,Java 内存模型)简化多线程编程、保证程序可移植性。如果 Java 没有 JMM,其行为就依赖于处理器本身的内存一致性模型,不同处理器可能差异很大,所以同样一段程序在处理器 A 上运行正常,并不能保证其在处理器 B 上也运行正常,这就违背了“书写一次,到处执行”的初衷。 2. JMM 中的 happen-before 是什么?happen-before 是 Java JMM 中保证多线程操作可见性的机制。它的具体表现形式,包括但远不止是我们直觉中的 synchronized、volatile、lock 操作顺序等方面,例如: 线程内执行的每个操作,都保证 happen-before 后面的操作,这就保证了基本的程序顺序规则,这是开发者在书写程序时的基本约定。 对于 volatile 变量,对它的写操作,保证 happen-before 在随后对该变量的读取操作。 对于一个锁的解锁操作,保证 happen-before 加锁操作。 对象的构建完成,保证 happen-before 于 finalizer 的开始动作。 3. JMM 是怎么解决可见性等问题的呢?JMM 内部的实现通常是依赖于所谓的内存屏障,通过禁止某些重排序的方式,提供内存可见性保证,也就是实现了各种 happen-before 规则。举例:对于一个 volatile 变量: 对该变量的写操作之后,编译器会插入一个写屏障。 对改变量的读操作之前,编译器会插入一个读屏障。 内存屏障能够在类似变量读、写操作之后,保证其他线程对 valatile 变量的修改对当前线程可见,或者本地修改对其他线程提供可见性。换句话说,线程写入,写屏障会通过类似强迫刷出处理器缓存的方式,让其他线程能够拿到最新数值。 4. 总结happen-before 是 Java JMM 中保证多线程操作可见性的机制。 5. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第28讲」谈谈你的 GC 调优思路|学习笔记","date":"2019-05-12T02:30:41.000Z","path":"2019/05/12/「第28讲」谈谈你的-GC-调优思路-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 28 篇学习笔记,坚持写完 36 讲学习笔记。GC 调优还是得具体问题具体分析,下文为整理的基础思路。 1. 基本的调优思路 理解应用需求和问题,确定调优目标。假设,我们的应用偶尔会出现性能抖动,出现较长的服务停顿。我们可以将目标简化为,GC 暂停尽量控制在 200ms 以内。 掌握 JVM 和 GC 的状态,定位具体的问题,确定真的有 GC 调优的必要。方法:通过 jstat 等工具查看 GC 等相关状态,开启 GC 日志等。 考虑 GC 类型是否符合我们的应用特征,如果符合,目前的问题是 Minor GC 过长,还是其他什么问题,如果不符合,是否可以切换其他 GC。 通过分析确定具体调整的参数或者软硬件配置。 验证是否达到调优目标,否则继续调优。 2. 调优建议 建议尽量升级到较新 JDK 版本; 掌握 GC 调优信息收集途径; 121.-XX:+PrintGCDetails2.-XX:+PrintGCDateStamps 3. 通用实践(G1) 如果发现 Young GC 非常耗时,这可能是因为新生代太大了,我们可以考虑减小新生代的最小比例。 1-XX:G1NewSizePercent 如果 Mixed GC 延迟较长,可以减少一次处理的 region 个数 1-XX:G1OldCSetRegionThresholdPercent 也可以提高 Mixed GC 的个数。1-XX:G1MixedGCCountTarget 4. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第27讲」Java 常见的垃圾收集器有哪些?|学习笔记","date":"2019-04-21T13:32:43.000Z","path":"2019/04/21/「第27讲」Java-常见的垃圾收集器有哪些?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 27 篇学习笔记,坚持写完 36 讲学习笔记。 首先我们必须清楚的知道,垃圾收集器(GC,Garbage Collector)是和具体 JVM 相关的,不同的厂商(IBM、\bOracle),不同版本的 JVM,提供的 GC 是不同的。接下来,我们主要看看 Oracle JDK。 1. Serial GC 简介最古老的垃圾收集器,“Serial”体现在其收集工作是单线程的。 优点精简的 GC 实现,没有复杂的数据结构,初始化也简单 缺点在进行垃圾收集过程中,必须暂停所有工作线程,会进入臭名昭著的“Stop-The-World”状态。 应用一直是 Client 模式下 JVM 的默认选项。例如:用在桌面应用程序,收集几十兆甚至几百兆的新生代,停顿时间完全可以控制在几十毫秒最多一百毫秒。 年代新生代为 Serial。通常将其老年代实现单独称作 Serial Old,它采用了标记-整理(Mark-Compact)算法。 2. ParNew GC Seriial GC 的多线程版本,新生代收集器。 3. CMS(Concurrent Mark Sweep)GC 简介基于标记-清除(Mark-Sweep)算法 优点较少的停顿时间 缺点采用标记-清除算法,存在内存碎片化问题,所以难免避免在长时间运行等情况下发生 full GC,导致恶劣的停顿。由于并发,CMS 会占用更多 CPU 资源,并和用户线程争抢。 4. Parallel GC 简介吞吐量优先的 GC,新生代和老年代 GC 都是并行的。吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间) 应用JDK 8 Server 模式 JVM 的默认 GC 5. G1 GC 简介一种兼顾吞吐量和停顿时间的 GC 实现 应用JDK 9 默认 GC 选项 6. 判断对象是否可以回收判断对象是否可以回收,主要有两种基本算法,引用计数和可达性分析 引用计数引用计数算法,就是为对象添加一个引用计数,用于记录对象被引用的情况,如果计数为 0,即表示对象可以回收。Python 选择该方式,Java 并没有选择引用计数,因为很难处理循环引用关系。 可达性分析其原理简单来说,就是将对象及其引用关系看作一个图,选定活动的对象作为 GC Roots,然后跟踪引用链条,如果一个对象和 GC Roots 之间不可达,也就是不存在引用链条,那么即可认为是可回收对象。JVM 会把虚拟机栈和本地方法栈中正在引用的对象、静态属性引用的对象和常量,作为 GC Roots。 7. 常见的垃圾收集算法 复制(Copying)算法新生代基本都是基于复制算法,将活着的对象复制到 to 区域,拷贝过程中将对象顺序放置,就可以避免内存碎片化。这么做的代价是,既然要进行复制,就要提前预留内存空间,有一定的浪费。 标记-清除(Mark-Sweep)首先进行标记工作,标识出所有要回收的对象,然后进行清除。会出现碎片问题,这就导致其不适合特别大的堆,否则,一旦出现 Full GC,暂停时间可能根本无法接受。 标记-整理(Mark-Compact)类似标记-清除,但为了避免内存碎片化,它会在清理过程中将对象移动,以确保移动后的对象占用连续的内存空间。 8. 实践1-Xmx4096M -Xms4096M -XX:PermSize=1024M -XX:MaxPermSize=4096M -Duser.timezone=GMT+08 -XX:ParallelGCThreads=4 -XX:CICompilerCount=2 我们看一个 JVM 参数,如上图。该参数应用于 JDK 8启动时提示:12Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=1024M; support was removed in 8.0Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=4096M; support was removed in 8.0 我们可以知道 -XX:PermSize=1024M -XX:MaxPermSize=4096M 两个参数不生效,因为 JDK 8 已经移除了 permanent generation(永久代)。ParallelGCThreads,表示 JVM 在进行并行 GC 的时候,用于 GC 的线程数。CICompilerCount,表示最大并行编译数。 9. 总结Serial GC 单线程,结构简单,是 Client 模式首选,Parallel GC 是 jdk8 server JVM 默认选项,吞吐量优先,而 CMS 适合,B/S 架构,现在已经逐步淘汰,G1 是 jdk9 默认选项,目前是较好的选择。 10. 参考文档《Java 核心技术 36 讲》《深入理解 Java 虚拟机》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"我要开始打卡了","date":"2019-04-13T06:02:41.000Z","path":"2019/04/13/我要开始打卡了/","text":"近两年知识付费火了,作为一个程序员(终身学习职业)买了不少《极客时间》的课程,完成度一般吧。我个人一直认为组队学习是效果最好的,这次便参加了骨灰级程序员左耳听风的 ARTS 打卡。为什么做这件事,怎么做这件事,记录一下,以便要放弃时,回来看看,激励自己。 为什么做这件事情 100 天刻意练习,感受习惯的力量 最有效的学习方法,是经过思辨,践行,总结和归纳,真正输出自己的知识地图。 做什么(ARTS[每周]) Algorithm 美 [ˈælɡəˌrɪðəm]做至少一个 LeetCode 的算法题,主要为了编程训练和学习。leetcode Review阅读并点评至少一篇英文技术文章,主要为了学习英文。medium Tip学习至少一个技术技巧,主要为了总结归纳,日常工作中所遇到的知识点。Tip Share分享一篇有观点和思考的技术文章,主要为了建立影响力,能够输出价值观。微信公众号「ETLshow」 左耳听风|ARTS召集令我的打卡位置","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第26讲」如何监控和诊断 JVM 堆内和对外内存使用?|学习笔记","date":"2019-04-13T05:14:57.000Z","path":"2019/04/13/「第26讲」如何监控和诊断-JVM-堆内和对外内存使用?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 26 篇学习笔记,坚持写完 36 讲学习笔记。 1. 工具 综合性的图形化工具 JConsole VisualVM 命令行工具 jstat jmap Java Mission Control(JMC)不仅仅能够使用 JMX 进行普通的管理、监控任务,还可以配合 Java Flight Recorder(JFR)技术,以非常低的开销,收集和分析 JVM 底层的 Profiling 和事件等信息。 一个特殊的部分,就是堆外内存中的直接内存,前面的工具基本不适用,使用 Native Memory Tracking (NMT),它会从 JVM 本地内存分配的角度进行解读。 2. 堆内部结构按照 GC 年代划分,Java 堆分为: 1.新生代(Young Generation)新生代是大部分对象创建和销毁的区域。 Eden[‘i:dn]伊甸园,对象初始分配的区域。 Survivor[sərˈvaɪvə(r)]幸存者,用来放置从 Minor[ˈmaɪnɚ]GC 中保留下来的对象。 2.老年代(Old Generation)放置长生命周期对象,通常都是从 Survivor 区域拷贝过来的对象。如果对象太大,完全无法在新生代找到足够长的连续空闲空间,JVM 就会直接分配到老年代。 3.永久代(Permenent Generation)早期 Hotspot JVM 方法区的实现方式,jdk 8 以后就没有了。 3. JVM 参数123456789101112131415161.最大堆体积-Xmx value2.初始最小堆体积-Xms value3.老年代和新生代的比例-XX:NewRatio=value默认数值是 2,即老年代是新生代的 2 倍,即新生代是堆的 1/3。4.直接设置新生代大小-XX:NewSize=value5.Eden 和 Survivor 的比例-XX:SurvivorRatio=value如果这个数值是 8,即 Eden 是 Survivor 的 8 倍 在 JVM 内部,如果 -Xms 小于 -Xmx,堆的大小并不会直接扩展到上限,当内存需求不断增加后,JVM 会逐渐扩展新生代等区域的大小。 4. 总结堆内部按照 GC 年代划分,分为新生代、老年代及永久代(jdk 8 以后已经没了),新生代中分为 Eden 和 Survior。 5. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第25讲」谈谈 JVM 内存区域划分,哪些区域可能发生 OutOfMemoryError?|学习笔记","date":"2019-03-31T10:20:20.000Z","path":"2019/03/31/「第25讲」谈谈-JVM-内存区域划分,哪些区域可能发生-OutOfMemoryError-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 25 篇学习笔记,坚持写完 36 讲学习笔记。 1. JVM 内存区域的划分 程序计数器(PC,Program Counter Register)程序计数器会存储当前线程正在执行的 Java 方法的 JVM 指令地址,如果是在执行本地方法,则是未指定值(undefine)。在 JVM 规范中,每个线程都有它自己的程序计数器,并且任何时间一个线程都只有一个方法在执行,即当前方法。 Java 虚拟机栈(Java Virtual Machine Stack)每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的 Java 方法调用。在一个时间点,对应的只会有一个活动的栈帧,通常叫作当前帧,方法所在的类叫作当前类。栈帧中存储着局部变量表、操作数栈、动态链接、方法正常退出或者异常退出的定义等。 本地方法栈(Native Method Stack)与 Java 虚拟机栈类似,支持对本地方法的调用,也是每个线程都会创建一个。 堆(Heap)它是 JVM 内存管理的核心区域,GC 堆,用来放置 Java 对象实例。堆,被所有线程共享。 方法区(Method Area)用于存储所谓的元(Meta)数据,例如类结构信息,以及对应的运行时常量池、字段、方法代码等。方法区,被所有线程共享。 运行时常量池(Run-Time Constant Pool)是方法区的一部分。存放各种常量信息,不管是编译器生成的各种字面量,还是需要在运行时决定的符号引用。 2. Java 进程内存占用第 1 点,是 JVM 规范中定义的 JVM 运行时内存占用,规范侧重的是通用的、无差别的部分,对于应用开发者来说,只要是 JVM 进程在运行时会占用,都会影响到我们的工程实践。因此需要补充两点: 直接内存(Direct Memory)Direct Buffer 所直接分配的内存。 Code CacheJVM 本身是个本地程序,还需要其他的内存去完成各种基本任务,比如,JIT Compiler 在运行时对热点方法进行编译,就会将编译后的方法存储在 Code Cache 里面;GC 等功能需要运行在本地线程之中,类似部分都要占用内存空间。 3. 哪些区域可能发生 OOMOOM 就是 JVM 内存不够用了,即没有空闲内存,并且垃圾收集器也无法提供更多内存。 除了程序计数器,其他区域都可能会因为可能的空间不足发生 OOM 。 堆错误信息:”java.lang.OutOfMemoryError:Java heap space”原因:可能存在内存泄露问题、可能堆的大小不合理 Java 虚拟机栈和本地方法栈错误信息:”StackOverFlowError” “OutOfMemoryError”原因:可能由于不断的进行递归调用 4. 总结画个图就很直观了,《Java 核心技术 36 讲》的图真的不错。 5. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第15讲」synchronized 和 ReentrantLock 有什么区别|学习笔记","date":"2019-03-01T15:16:43.000Z","path":"2019/03/01/「第15讲」synchronized-和-ReentrantLock-有什么区别-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 15 篇学习笔记,坚持写完 36 讲学习笔记。 1. synchronized 和 ReentrantLock 有什么区别 synchronizedsynchronized 是 Java 内建的同步机制,它提供了互斥的语义和可见性,当一个线程已经获取当前锁时,其他视图获取的线程只能等待或者阻塞在那里。 ReentrantLockReentrantLock,通常翻译为再入锁,它的语义和 synchronized 基本相同。再入锁通过代码直接调用 lock() 方法获取,代码书写也更加灵活。于此同时,ReentrantLock 提供了很多实用的方法,能够实现很多 synchronized 无法做到的细节控制,比如可以控制 fairness,也就是公平性。但是,编码中也需要注意,必须要明确调用 unlock()方法释放,不然就会一直持有该锁。 性能早期版本 synchronized 在很多场景下性能相差较大,在后续版本进行了较多改进,在低竞争场景中表现可能优于 ReentrantLock。 2. 线程安全线程安全是一个多线程环境下正确性的概念,也就是保证多线程环境下共享的、可修改的状态的正确性。 换个角度,如果状态不是共享的,或者状态是不可修改的,那么也就不存在线程安全问题。 线程安全需要保证几个基本特性 原子性相关操作不会中途被其他线程干扰,一般通过同步机制实现。 可见性一个线程修改了某个共享变量,其状态能够立即被其他线程知晓,通常被解释为将线程本地状态反映到主内存上,volatile 就是负责保证可见性的。 有序性保证线程内串行语义,避免指令重排等。 3. 多线程举例12345678910111213141516171819202122232425262728293031public class ThreadSafeSample {public int sharedState;public void nonSafeAction() {while (sharedState < 100000) {int former = sharedState++;int latter = sharedState;if (former != latter - 1) {System.out.printf(\"Observed data race, former is \" +former + \", \" + \"latter is \" + latter);}}}public static void main(String[] args) throws InterruptedException {ThreadSafeSample sample = new ThreadSafeSample();Thread threadA = new Thread(){public void run(){sample.nonSafeAction();}};Thread threadB = new Thread(){public void run(){sample.nonSafeAction();}};threadA.start();threadB.start();threadA.join();threadB.join();}} 运行结果:1Observed data race, former is 13097, latter is 13099 通过结果,可以看到出现线程安全问题。我们将两次赋值过程用 synchronized 保护起来,使用 this 作为互斥单元,就可以避免别的线程并发的去修改 sharedState。12345synchronized (this) {int former = sharedState ++;int latter = sharedState;// …} 4. ReentrantLock 关于再入它是表示当一个线程试图获取一个它已经获取的锁时,这个动作就自动成功,这是对锁获取粒度的一个概念,也就是锁的持有是以线程为单位而不是基于调用次数。 公平性(fairness)再入锁可以设置公平性(fairness),我们可以在创建再入锁时选择是否是公平的。 1ReentrantLock fairLock = new ReentrantLock(true); 公平性是指在竞争场景中,当公平性为真时,会倾向于将锁赋予等待时最久的线程。 若要保证公平性则会引入额外开销,自然会导致一定的吞吐量下降。 ReentrantLock 使用时,注意保证锁释放,每一个 lock()动作,我建议都立即对应一个 try-finally。1234567ReentrantLock fairLock = new ReentrantLock(true);// 这里是演示创建公平锁,一般情况不需要。fairLock.lock();try {// do something} finally {fairLock.unlock();} 5. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第14讲」谈谈你知道的设计模式|学习笔记","date":"2019-02-16T08:50:53.000Z","path":"2019/02/16/「第14讲」谈谈你知道的设计模式-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 14 篇学习笔记,坚持写完 36 讲学习笔记。 理解和掌握典型的设计模式,有利于提高沟通、设计的效率和质量。 1. 设计模式分类大致按照模式的应用目标分类,设计模式可以分为创建型模式、结构型模式和行为型模式。 创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式(Factory、Abstract Factory)、单例模式(Singleton)、构建器模式(Builder)、原型模式(ProtoType)。 结构型模式,是针对软件设计结构的总结,关注于类、对象继承、组合方式的实践经验。常见的结构型模式,包括桥接模式(Bridge)、适配器模式(Adapter)、装饰者模式(Decorator)、代理模式(Proxy)、组合模式(Coposite)、外观模式(Facade)、享元模式(Flyweight)等。 行为型模式,是从类或对象之间交互、职责划分等角度总结的模式。比较常见的行为模式有策略模式(Strategy)、解释器模式(Interpreter)、命令模式(Command)、观察者模式(Observer)、迭代器模式(Iterator)、模版方法模式(Template Method)、访问者模式(Visitor)。 2. 单例设计模式第一版:123456public class Singleton {private static Singleton instance = new Singleton();public static Singleton getInstance() {return instance;}} 第一版没有私有构造,因此别人完全可以直接“new Singleton()”。第二版:12345678public class Singleton {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}} 第二版就是单例了,是“饿汉式”,还能有什么改进么?标准库中很多地方使用懒加载(lazy-load),改善初始内存,单例同样使用,下面是第三版:1234567891011public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}} 第三版,这个实现在单线程环境不存在问题,但是如果处于并发场景,就需要考虑线程安全,最熟悉的莫过于“双检锁”,其要点在于: valatile 能够保证可见性,以及保证 getInstance 返回的是初始化完全的对象。 在同步之前进行 null 检查,以尽量避免进入相对昂贵的同步块。 直接在 class 级别进行同步,保证线程安全的类方法调用。第四版:12345678910111213141516public class Singleton {private static volatile Singleton singleton = null;private Singleton() {}public static Singleton getSingleton() { if (singleton == null) { // 尽量避免重复进入同步块 synchronized (Singleton.class) { // 同步.class,意味着对同步类方法调用 if (singleton == null) { singleton = new Singleton(); } } } return singleton;}} 这第四版就是“饱汉式”,当然还有其它单例实现方式:枚举类、静态内部类等。上面是比较学究,其实实践中未必需要如此复杂,我们来看看 Java 核心类库自己的单例实现,比如 java.lang.Runtime。注意,它直接就是饿汉时,静态实例被声明为 final。12345678private static final Runtime currentRuntime = new Runtime();private static Version version;// …public static Runtime getRuntime() {return currentRuntime;}/** Don't let anyone else instantiate this class */private Runtime() {} 3. Spring 中设计模式的应用 BeanFactory 和 ApplicationContext 应用了工厂模式 在 Bean 的创建中,Spring 也为不同 scope 定义的对象,提供了单例和原型等模式实现。 AOP 领域则是使用了代理模式、装饰者模式、适配器模式等。 各种事件监听器,是观察者模式的典型应用。 类似 JdbcTemplate 等则是应用了模版方法模式。 4. 其它设计模式,我之前阅读过《大话设计模式》,当时在《简书》上做了笔记,有时间整理一下,当时的代码在,https://github.com/zmdstr/design-pattern。 5. 总结在学习开源框架的同时,学习设计模式是最好的方式。 6. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第13讲」接口和抽象类有什么区别|学习笔记","date":"2019-02-15T15:03:26.000Z","path":"2019/02/15/「第13讲」接口和抽象类有什么区别-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 13 篇学习笔记,坚持写完 36 讲学习笔记。 1.接口和抽象类有什么区别? 接口接口是对行为的抽象,它是抽象方法的集合,利用接口可以达到 API 定义和实现分离的目的。接口,不能实例化;不能包含任何非常量成员;没有非静态方法实现。 抽象类抽象类主要是代码重用,除了不能实例化,形式上和一般的 Java 类并没有太大区别,可以有一个或者多个抽象方法,也可以没有抽象方法。 2.接口 Mark Interface没有任何方法的接口,它的目的就是为了声明某些东西,比如我们熟知的 Cloneable、Serializable 等。它与 Annotation 有异曲同工之处,好处是简单直接。对于 Annotation,因为可以指定参数和值,在表达能力上要更强大一些,所以更多人选择使用 Annotaion。 Functional InterfaceJava 8 增加了函数式编程的支持,所谓 functional interface,简单说就是只有一个抽象方法的接口,通常建议使用 @FunctionalIntercace Annotation 来标记。 default methodJava 8 以后,接口也是可以有方法实现的。Java 8 中添加了一系列 default method,主要是增加 Lambda、Stream 相关功能。 3.面向对象设计面向对象设计的基本要素:封装、继承、多态。 设计原则,S.O.L.I.D 原则。 单一职责(Single Responsibility)类或者对象最好只有单一职责,在程序设计中如果发现某个类承担着多种义务,可以考虑进行拆分。 开关原则(Open-Close,Open for extension,close for modification)设计要对扩展开放,对修改关闭。 里氏替换(Liskov Substitution)进行继承关系抽象时,凡是可以用父类或者基类的地方,都可以用子类替换。 接口分离(Interface Segregation)在进行类和接口设计时,如果在一个接口里定义了太多方法,其子类可能面临两难,就是只有部分方法对它是有意义的。这就破坏了程序的内聚性。对于这种情况,可以通过拆分成功能单一的多个接口,讲行为进行解耦。 依赖反转(Dependency Inversion)实体应该依赖抽象而不是实现。也就是高层次模块,不应该依赖低层次模块,而是应该基于抽象。实践这一原则是保证产品代码之间适当耦合度的法宝。 我们来看一个代码改进例子。12345678910public class VIPCenter {void serviceVIP(T extend User user>) {if (user instanceof SlumDogVIP) {// 穷 X VIP,活动抢的那种// do somthing} else if(user instanceof RealVIP) {// do somthing}// ...} 利用开闭原则,进行改造12345678910111213141516171819public class VIPCenter {private Map<User.TYPE, ServiceProvider> providers;void serviceVIP(T extend User user) {providers.get(user.getType()).service(user);}}interface ServiceProvider{void service(T extend User user) ;}class SlumDogVIPServiceProvider implements ServiceProvider{void service(T extend User user){// do somthing}}class RealVIPServiceProvider implements ServiceProvider{void service(T extend User user) {// do something}} 4.参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第12讲」Java 有几种文件拷贝方式?哪一种最高效?|学习笔记","date":"2019-02-14T09:55:42.000Z","path":"2019/02/14/「第12讲」Java-有几种文件拷贝方式?哪一种最高效?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 12 篇学习笔记,坚持写完 36 讲学习笔记。 我们之前学习了 Java IO 体系,BIO、NIO、AIO,今天我们来看看其它不可忽略的部分。 1.Java 有几种文件拷贝方式? 利用 java.io 类库,直接为源文件构建一个 FileInputStream 读取,然后再为目标文件构建一个 FileOutputStream,完成写入工作。 1234567891011public static void copyFileByStream(File source, File dest) throwsIOException {try (InputStream is = new FileInputStream(source);OutputStream os = new FileOutputStream(dest);){byte[] buffer = new byte[1024];int length;while ((length = is.read(buffer)) > 0) {os.write(buffer, 0, length);}}} 利用 java.nio 类库提供的 transferTo 或 transferFrom 方法实现 12345678910111213public static void copyFileByChannel(File source, File dest) throwsIOException {try (FileChannel sourceChannel = new FileInputStream(source).getChannel();FileChannel targetChannel = new FileOutputStream(dest).getChannel();){for (long count = sourceChannel.size() ;count>0 ;) {long transferred = sourceChannel.transferTo(sourceChannel.position(), count, targetChannel); sourceChannel.position(sourceChannel.position() + transferred);count -= transferred;}}} Java 标准类库本身提供的 File.copy 实现 2.哪一种文件拷贝方式最高效?总体上来说,NIO transferTo/From 的方式可能更快,因为它能利用现代操作系统底层机制,避免不必要拷贝和上下文切换。 3.不同拷贝方法,本质上有什么明显的区别 用户态空间(User Space)操作系统内核、硬件驱动等运行在内核态空间,具有相对高的特权 内核态空间(Kernel Space)而内核态空间,则是给普通应用和服务使用。 当我们使用输入输出流进行读写时,实际上是进行了多次上下文切换,比如应用读取数据时,先在内核态将数据从磁盘读取到内核缓存,再切换到用户态将数据从内核缓存读取到用户缓存。写入操作也类似,仅仅是步骤相反,参考下图:所以,这种方式会带来一定的额外开销,可能会降低 IO 效率。 而基于 NIO transferTo 的实现方式,在 Linux 和 Unix 上,则会使用到零拷贝技术,数据传输并不需要用户态参与,省去了上下文切换的开销和不必要的内存拷贝,进而可能提供应用拷贝性能。 注意,transferTo 不仅仅是可以用在文件拷贝中,与其类似的,读取磁盘文件,然后进行 Socket 发送,同样可以享受这种机制带来的性能和拓展性提高。 4. 总结Java IO 一直是比较难学习的部分,这两节对课程里的内容做了简化,想要更深入的学习,请到极客时间订阅课程。 今天讲了 Java 文件拷贝方式,第一种,使用输入输出流的方式,这种方式涉及用户态和内核态的切换,效率不高。第二种,NIO transferTo,这种方式,更加底层,使用零拷贝,不涉及用户态、内核态切换,效率高。而第三种,Java 类库的 File.copy 最终也是用的用户态的流,效率不高。 5. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第11讲」02 NIO 如何实现多路复用?|学习笔记","date":"2019-02-13T15:15:54.000Z","path":"2019/02/13/「第11讲」02-NIO-如何实现多路复用?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 11 篇学习笔记,坚持写完 36 讲学习笔记。 1.NIO 介绍NIO 主要组成部分: Buffer高效的数据容器 Channel类似在 Linux 之类操作系统上看到的文件描述符,是 NIO 中被用来支持批量式 IO 操作的一种抽象。File 或者 Socket,是 BIO 中的概念,通常被认为是比较高层次的抽象,而 Channel 则是更加操作系统底层的一种抽象,这也使得 NIO 得以充分利用现代操作系统底层机制,获得特定场景的性能优化,例如 DMA(Direct Memory Access)等。不同层次的抽象是相互关联的,我们可以通过 Socket 获取 Channel,反之亦然。 SelectorNIO 实现多路复用的基础,它提供了一种高效的机制,可以检测到注册在 Selector 上的多个 Channel 中,是否有 Channel 处于就绪状态,进而实现了单线程对多 Channel 的高效管理。 Chartset提供 Unicode 字符串定义 2.NIO 能解决什么问题?我们来码代码,构建一个服务器应用,只简单要求能够同时服务多个客户端请求即可。 BIO 方式12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273public class ServerDemo extends Thread {private ServerSocket serverSocket;public int getPort() {return serverSocket.getLocalPort();}@Overridepublic void run() {try {// 0 表示自动绑定一个空闲端口serverSocket = new ServerSocket(0);ExecutorService executor = Executors.newFixedThreadPool(8);while (true) {// 阻塞等待客户端连接Socket socket = serverSocket.accept();RequestHandler requestHandler = new RequestHandler(socket);/*** 当连接建立后,启动一个线程,负责回复客户端请求* 每次创建一个线程,消耗内存,改为下面的使用线程池*///requestHandler.start();/*** 启用线程池* 通过一个固定大小的线程池管理工作线程,避免频繁创建、销毁线程的开销*/executor.execute(requestHandler);}} catch (IOException e) {e.printStackTrace();} finally {if (serverSocket != null) {try {serverSocket.close();} catch (IOException e) {e.printStackTrace();}}}}class RequestHandler extends Thread {private Socket socket;RequestHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {try (PrintWriter out = new PrintWriter(socket.getOutputStream())) {out.println(\"Hello world!\");out.flush();} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {ServerDemo server = new ServerDemo();server.start();try (Socket client = new Socket(InetAddress.getLocalHost(), server.getPort())) {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream()));bufferedReader.lines().forEach(s -> System.out.println(s));} catch (Exception e) {e.printStackTrace();}}} 一个客户端请求过来,在服务端对应一个线程,如果连接数并不是非常多,只有最多几百个连接的普通应用,这种模式往往可以工作的很好,但是当连接数量急剧上升,这种方式就无法很好地工作了,因为线程上下文切换开销在高并发时变得很慢,这是同步阻塞方式的地扩展性劣势。 NIO 方式123456789101112131415161718192021222324252627282930313233343536373839404142434445public class NIOServer extends Thread {@Overridepublic void run() {try (Selector selector = Selector.open();ServerSocketChannel serverSocket = ServerSocketChannel.open()) {serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 9988));serverSocket.configureBlocking(false);serverSocket.register(selector, SelectionKey.OP_ACCEPT);while (true) {selector.select();Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();sayHelloWorld((ServerSocketChannel) key.channel());iterator.remove();}}} catch (Exception e) {e.printStackTrace();}}private void sayHelloWorld(ServerSocketChannel server) {try (SocketChannel client = server.accept()) {client.write(Charset.defaultCharset().encode(\"hello world\"));} catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) {NIOServer nioServer = new NIOServer();nioServer.start();try (Socket client = new Socket(InetAddress.getLocalHost(), 9988)) {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream()));bufferedReader.lines().forEach(s -> System.out.println(s));} catch (Exception e) {e.printStackTrace();}}} NIO 利用了单线程轮询事件的机制,通过高效地定位就绪的 Channel,来决定做什么,仅仅 select 阶段是阻塞的,可以有效避免大量客户端连接时,频繁线程切换带来的问题。应用的扩展能力有了非常大的提高。 3.参考文档《Java 核心技术 36 讲》代码已上传:https://github.com/zmdstr/java-tec-36-demo","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第11讲」01 Java提供了哪些IO方式?|学习笔记","date":"2019-02-11T03:48:58.000Z","path":"2019/02/11/「第11讲」01-Java提供了哪些IO方式?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 11 篇学习笔记,坚持写完 36 讲学习笔记。 1.Java 提供了哪些 IO 方式根据交互方式分类如下: BIO 同步阻塞 IO传统的 java.io 包,提供了我们最熟悉的一些 IO 功能,比如 File 抽象、输入输出流等。在读取输入流或者写入输出流时,在读、写动作完成之前,线程会一直阻塞在那里。优点:代码简单、直观,缺点:效率及扩展性存在局限性,容易成为应用性能瓶颈。java.net 下面提供的部分网络 API,比如 Socket、ServerSocket、HttpURLConnection 同样归类到同步阻塞 IO 类库,因为网络通信同样是 IO 行为。 NIO 同步非阻塞 IOJava 1.4 中引入了 NIO 框架(java.nio 包),提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层的高性能数据操作方式。 NIO2(AIO) 异步非阻塞 IOJava 7 中,NIO 进一步优化,也就是 NIO 2,引入了异步非阻塞 IO 方式(AIO)。异步 IO 操作基于事件和回调机制,可以简单理解为,应用操作直接返回,而不会阻塞在那里,当回台处理完成,操作系统会通知相应线程进行后续工作。 2.同步与异步(synchronous/asynchronous)简单来说,同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调用返回,才会进行下一步;而异步则相反,其它任务不需要等待当前调用返回,通常依靠事件、回调等机制来实现任务间次序关系。举例:RPC 调用就是同步的,等待返回结果,而发Q(消息)则是异步的。 3.阻塞与非阻塞(blocking/non-blocking)这个简单,在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如写入操作完成;而非阻塞则是不管 IO 操作是否结束,直接返回,相应操作在后台继续处理。 4. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第10讲」如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?","date":"2019-02-10T13:20:09.000Z","path":"2019/02/10/「第10讲」如何保证集合是线程安全的-ConcurrentHashMap如何实现高效地线程安全?/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 10 篇学习笔记,坚持写完 36 讲学习笔记。 1.如何保证容器是线程安全的?Java 提供了不同层面的线程安全支持。 集合框架内部提供了同步容器,如:Vector、HashTable 等。 提供了同步包装器,可以调用 Collections 工具类提供的包装方法,来获取一个同步的包装容器(如 Collections.synchronizedMap) 并发包提供的线程安全容器,如 ConcurrentHashMap、CopyOnWriteArrayList、ArrayBlockingQueue 2.为什么需要 ConcurrentHashMap? Hashable 本身比较低效,一个线程在进行同步操作时,其它线程只能等待,大大降低了并发操作的效率。 同步包装器只是利用输入 Map 构造了另一个同步版本,利用 “this”作为互斥的 mutex,没有真正意义上的改进。 3.早期 ConcurrentHashMap 的设计早期 ConcurrentHashMap,其实现是基于: 分离锁,也就是将内部进行分段(Segment),里面则是 HashEntry 的数组,和 HashMap 类似,哈希相同的条目也是以链表形式存放。可以理解为将 HashMap 的数组进行了分段,每段就是一个 Segment。 HashEntry 内部使用 volatile 的 value 字段来保证可见性。参考下图,其核心是利用分段设计,在进行并发操作的时候,只需要锁定相应段,这样就有效避免了类似 Hashtable 整体同步的问题,大大提供了性能。 4.Java 8 之后的版本中,ConcurrentHashMap 发生了那些变化? 总体结构上,它的内部存储变得和 HashMap 结构非常相似,同样是大的桶(bucket)数组,然后内部也是一个个所谓的链表结构(bin),同步的粒度要更细致一些。 内部仍然有 Segment 定义,仅用来保证兼容性,不再有任何结构上的用处。 不再使用 Segment,初始化操作大大简化,修改为 lazy-load 形式,避免了初始开销。 数据存储利用 volatile 来保证可见性。 使用 CAS 等操作,在特定场景进行无锁并发操作。 使用 Unsafe、LongAdder 这类底层手段,进行了极端情况的优化。 5. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第9讲」对比 HashTable、HashMap、TreeMap 有什么区别|学习笔记","date":"2019-02-09T14:01:38.000Z","path":"2019/02/09/「第9讲」对比-HashTable、HashMap、TreeMap-有什么区别-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 9 篇学习笔记,坚持写完 36 讲学习笔记。 1.对比 HashTable、HashMap、TreeMap 有什么区别?相同点:都是以键值对形式存储、操作数据的容器类型不同点: HashTableHashTable 是早期 Java 类库提供的一个哈希表实现,线程安全,不支持 null 键和 null 值,由于同步导致的性能开销大,所以已经很少被推荐使用。 HashMapHashMap 是应用更加广泛的哈希表实现,非线程安全,支持 null 键和 null 值。通常情况下,HashMap 进行 put 或者 get 操作,可以达到常数时间复杂度的性能。 TreeMapTreeMap 是基于红黑树的一种提供顺序访问的 Map,和 HashMap 不同,它的 get、put、remove 等操作都是 log(n) 时间复杂度。 2.Map 整体架构 3.HashMap 内部实现HashMap 可以看作是数组(Node<K,V>[]table)和链表结合组成的复合结构,数组被分为一个个桶(bucket),通过 key 哈希值决定了键值对在这个数组的寻址;哈希值相同的键值对,则以链表形式存储,当链表的长度超过阈值,链表会被改造为树形结构。 4.HashMap 初始化从非拷贝构造函数的实现来看,数组并没有在最初就初始化好,仅仅设置了一些初始化值而已。12345public HashMap(int initialCapacity, float loadFactor){// ...this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);} HashMap 是按照 lazy-load 原则,在首次使用时被初始化,下面为 put 方法实现。123public V put(K key, V value) {return putVal(hash(key), key, value, false, true);} 1234567891011121314final V putVal(int hash, K key, V value, boolean onlyIfAbent,boolean evit) {Node<K,V>[] tab; Node<K,V> p; int , i;if ((tab = table) == null || (n = tab.length) = 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == ull)tab[i] = newNode(hash, key, value, nll);else {// ...if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for firsttreeifyBin(tab, hash);// ...}} 通过上面代码我们可以看到: 如果表格是 null,resize 方法会负责初始化它; resize 方法兼顾两个职责,创建初始化表格,或者在容量不满足需求的时候,进行扩容 12if (++size > threshold)resize(); 具体键值对在哈希表中的位置(数组 index)取决于下面的位运算: 1i = (n - 1) & hash 注意这个 hash 来源于 HashMap 内部的 hash(key) 方法,我们看一下这个 hash 方法:1234static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>>16;} 为什么这里需要将高位数据移位到低位进行异或运算呢?这是因为有些数据计算出的哈希值差异主要在高位,而 HashMap 里的哈希寻址是忽律容量以上的高位的,这样处理就可以有效避免类似情况下的哈希碰撞。 5.resize 方法12345678910111213141516171819202122final Node<K,V>[] resize() {// ...else if ((newCap = oldCap << 1) < MAXIMUM_CAPACIY &&oldCap >= DEFAULT_INITIAL_CAPAITY)newThr = oldThr << 1; // double there// ...else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;else {// zero initial threshold signifies using defaultsfultsnewCap = DEFAULT_INITIAL_CAPAITY;newThr = (int)(DEFAULT_LOAD_ATOR* DEFAULT_INITIAL_CAPACITY;}if (newThr ==0) {float ft = (float)newCap * loadFator;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold = newThr;Node<K,V>[] newTab = (Node<K,V>[])new Node[newcap];table = n;// 移动到新的数组结构 e 数组结构} 门限值等于(负载因子)*(容量) 门限通常是以倍数进行调整 扩容后,需要将老的数组中的元素重新放置到新的数组,这是扩容的一个主要开销来源 6.容量、负载因子和树化 为什么关注容量、负载因子因为容量和负载因子决定了可用的桶数量,空桶太多会浪费空间,如果使用的太满则会严重影响操作的性能。极端情况下,假设只有一个桶,那么它就会退化成了链表,完全不能提供所谓常数时间复杂度的性能。 如何设置容量应该满足,容量大于”预估元素数量/负载因子”,同时是 2 的幂数。 12345678final void treeifyBin(Node<K,V>[] tab, int hash) {int n, index; Node<K,V> e;if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)resize();else if ((e = tab[index = (n - 1) & hash]) != null) {// 树化改造逻辑}} 上面是精简的 treeIfyBin,当 bin 的数量大于 TREEIFY_THRESHOLD时: 如果容量小于 MIN_TREEIFY_CAPACITY,只会进行简单的扩容 如果容量大于 MIN_TREEIFY_CAPACITY,则会进行树化 8.参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第8讲」对比 Vector、ArrayList、LinkedList 有何区别|学习笔记","date":"2019-02-08T14:24:31.000Z","path":"2019/02/08/「第8讲」对比-Vector、ArrayList、LinkedList-有何区别-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 8 篇学习笔记,坚持写完 36 讲学习笔记。今天开始改变学习笔记框架,不再参考 app 中讲解架构,问题导向。 在我目前的编程中,ArrayList 用的最多,LinkedList 偶尔使用,Vector 没有用过。下面通过几个问题开始我们的学习吧。 问题1:对比 Vector、ArrayList、LinkedList 有何区别相同点:Vector、ArrayList、LinkedList 都是 List 实现,因此都具有如下特性: 都提供按照位置进行定位、添加或者删除的操作; 都提供迭代器以遍历其内容。 不同点: Vector 是 Java 早期提供的线程安全的动态数组; ArrayList 是应用更加广泛的动态数组,不是线程安全的,因此性能要好很多;ArrayList 根据需要自动调整容量,扩容 50%,而 Vector 扩容 100%; LinkedList 是 Java 提供的双向链表,不需要调整容量,线程不安全。 问题2: Vector、ArrayList、LinkedList 容器适用场景Vector、ArrayList 作为动态数组,其内部元素以数组形式按顺序存储,非常适合随机访问的场景,除了尾部插入或者删除元素,其它位置插入或者删除元素,性能往往较差,比如我们在中间插入一个元素,要移动后续所有元素。而 LinkedList 进行节点插入、删除要高效的多,但随机访问性能要差的多。 问题3:集合框架的整体设计上图中,不包含 map,不包含并发容器。我们从图中可以看到,Collection 接口是集合的根,由其派生三大部分,List、Set、Queue。 ListList 就是我们前面介绍最多的有序集合,提供了方便的按位置定位、添加和删除。 Set与 List 最大的区别,不允许重复元素的出现。 QueueJava 提供的标准队列实现,除了集合的基本功能,还提供先进先出,后进先出等特定行为。 各种集合的通用逻辑都被抽象到相应的抽象类中。 问题四:HashSet、TreeSet、LinkedHashSet 容器适用场景 TreeSet 支持自然顺序访问,但是添加、修改、包含等操作相对低效,时间复杂度 log(n); HashSet 利用 Hash 算法,正常情况下,如果哈希散列正常,可以通过常数时间复杂度的添加、修改、包含等操作,但不保证顺序。 LinkedHashSet 内部构建了一个记录插入顺序的的双向链表,因此提供了按照插入顺序的遍历的能力,与此同时也提供了常数时间复杂度的添加、修改、包含等操作,但这些操作性能略低于 HashSet 毕竟需要维护链表。 遍历元素时,HashSet 性能受容量影响,初始化时,尽量指定大小,LinkedHashSet 遍历只和元素个数又关系。 问题五:这些线程不安全的容器类,能否支持并发场景答案是可以的,可以通过 Collections 工具类提供的一系列方法,例如1static <T> List<T> synchronizedList(List<T> list) 1List list = Collections.synchronizedList(new ArrayList()); 它的实现,基本就是将每个基本方法,比如 get、set、add 之类,都通过 synchronizd 添加基本的同步支持,非常简单粗暴,但也非常实用。 问题六:Java 提供的集合的默认排序算法 对于原始数据类型目前使用的是双轴快速排序,一种基于快速排序的改进算法。 对于对象数据类型目前则是使用 TimSort,思想上也是一种归并和二分插入排序结合的优化排序算法。 问题七:集合框架的演进和发展 Java 8Java 8 引入了并行排序算法,充分利用现代多核处理器计算能力,底层基于 fork-join 框架。引入 Lambda 和 Stream ,极大的方便了集合的操作。 Java 9在 Java 9 中,Java 标准类库提供了一系列的静态工厂方法,比如,List.of()、Set.of(),大大简化了构建小的容器实例的代码量。","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第7讲」int 和 Integer 有什么区别|学习笔记","date":"2019-02-07T14:17:45.000Z","path":"2019/02/07/「第7讲」int-和-Integer-有什么区别-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 7 篇学习笔记,坚持写完 36 讲学习笔记。 数据类型是程序设计语言描述事物、对象的方法。Java 数据类型分为内置类型和扩展类型两大类。 1.问题int 和 Integer 有什么区别?谈谈 Integer 的值的缓存范围 2.典型回答int 是 Java 8 个基本数据类型中的整形。Integer 是 int 的包装类,是引用数据类型。使用传统方法构建 Integer 对象,即 new 出来的 Integer 不进行缓存,而通过静态工厂方法 valueOf 获得的 Integer 对象会进行缓存,默认缓存范围是 -128 到 127 之间。 3.扩展3.1 intJava 保留了基本数据类型,但这主要是为满足 Java 高效率执行的要求。从编程者角度看 Java 不是纯粹的面向对象语言,但是从 JVM 角度看 Java 是纯粹的面向对象编程语言(详见参考文档一);下面程序需要 9 秒,但当我把 Long 改成 long 之后,0 秒内就完成了123456789public static void main(String[] args) {long millis = System.currentTimeMillis();Long sum = 0L; // uses Long, not longfor (long i = 0; i <= Integer.MAX_VALUE; i++) {sum += i;}System.out.println(sum);System.out.println((System.currentTimeMillis() - millis) / 1000);} 3.2 Integer提供这些类包裹的原因主要是为了在某些接口上提供和类(对象)一致的接口 12345678@Testpublic void myTest() {System.out.println(new Integer(3)==(new Integer(3))); // falseSystem.out.println(new Integer(100)==(new Integer(100)));// false//[-128; 127] 这个区间的265个整数会被 JVM 缓存存放, 所以在这个区间, JVM返回相同的对象System.out.println(Integer.valueOf(5)==(Integer.valueOf(5)));// trueSystem.out.println(Integer.valueOf(200)==(Integer.valueOf(200)) );//false} new 出来的对象是在堆内存中,返回的永远是不同的对象,但是当整数范围在 -128 和 127之间时,Integer.valueOf 返回的是同一个对象。 12345public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);} Integer 可以区分出未赋值和值为 0 的区别,int 则无法表达出未赋值的情况。例如,要想表达出没有参加考试和考试成绩为 0 的区别,则只能使用 Integer 。很多方法参数就只接收对象类型(Object) 还比如泛型就只支持对象类型。如果在数据库操作中某数值列允许为 null 的话,那接受该列数据的类型就只能为 Integer,而不能为 int,所以在一般性的持久化对象中尽量少地使用基本类型,除非确定它不会为空。 3.3 语法糖可以简单理解为 Java 平台为我们自动进行了一些转换,保证不同的写法在运行时等价,它们发生在编译阶段,也就是生成的字节码是一致的。 3.4 自动装箱、自动拆箱发生在什么阶段?自动装箱及拆箱是 java 语法糖,会自动转换为 Integer.valueOf()及 Integer.intValue() 发生在编译阶段。 3.5 自动装箱时,缓存机制起作用么?同上,语法糖,因此起作用 3.6 自动装箱、自动拆箱在实际编程中有什么需要注意的吗?原则上避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,创建 10 万个 Java 对象和 10 万个整数的开销可不是一个数量级上的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。 3.7 Java 对象要比原始数据类型开销大,那对象的内存结构是什么样子的? 在 HotSpot 虚拟机中,对象在内存中存储的布局可以分为 3 块区域:对象头(Header)实例数据(Instance Data)和对齐填充(Padding)。 4.总结int 基本数据类型,保留它是为了效率,Integer 包装类,提供对象相关操作,考虑泛型等操作。 5.参考文档《Java 核心技术 36 讲》《深入理解 Java 虚拟机》Java到底是不是一种纯面向对象语言Java/数据类型为什么有int 和integer","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"代码精进之路学习笔记一","date":"2019-01-19T03:33:02.000Z","path":"2019/01/19/代码精进之路学习笔记一/","text":"订阅了极客时间《代码精进之路》,今天开始写学习笔记,希望有所思考,有所收获。 看了一下课程目录,分为四篇,《认知篇》《“规范”修炼篇》《“经济”修炼篇》《“安全”修炼篇》,今天的笔记为《认知篇》的读书笔记,欢迎一起学习,共同进步。 你的名片-代码“你写的每一行代码,都是你的名片“。作为一个程序员,我们可以是一个手艺人,而代码就是我们的工艺品,精益求精,也就是这个课程的初衷,代码精进之路。不断打怪升级,不断打磨我们的工艺品。 优秀的代码每个程序员眼中,优秀的代码也许是不一样的。这个话题可能引起争论。作者给出了最后的一个概括,在软件的生命周期中,优秀的代码需要具备三个特征:经济、规范、安全,这也就是后面三篇的内容。 关于条件运算符简短的代码,不是优秀的代码,Kotlin 和 Go 均不支持条件表达式。 把错误关在笼子的五道关卡 程序员自己 编译器 回归测试 代码评审 代码分析 不同公司软件开发流程不同,代码经过的关卡不同,控制好关卡,把错误关在笼子里。 优秀的程序员简单罗列一下,作为努力目标 掌握一门编程语言 解决现实的问题 发现关键的问题 沉静的前行者 可以依赖的伙伴 时间管理者 总结作为一个程序员,代码是我们的名片,写优秀的代码,做优秀的程序员。 行胜于言践行才是最好的学习方式,虽然是认知篇,还是有需要践行的事情。 不使用复杂条件表达式 find Bug 插件使用起来 关注一下,测试代码覆盖率","tags":[{"name":"代码精进之路","slug":"代码精进之路","permalink":"http://freemeng.com/tags/代码精进之路/"}]},{"title":"「第6讲」动态代理是基于什么原理?|学习笔记","date":"2018-09-19T14:39:46.000Z","path":"2018/09/19/「第6讲」动态代理是基于什么原理-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 6 篇学习笔记,坚持写完 36 讲学习笔记。 关于动态代理,我之前接触的概念有:代理模式、静态代理、JDK 动态代理、CGLib 动态代理,ASM,今天来学习一下。 1. 问题谈谈 Java 反射机制,动态代理基于什么原理? 2. 典型回答反射是 Java 语言的一个高级特性,赋予程序在运行时自省的能力。通过反射,我们可以获取类及方法上的注解,创建对象,调用其方法。 动态代理,一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用动态代理实现的,例如面向切面编程(AOP)。 实现动态代理的方式有多种,JDK 动态代理是基于反射机制实现的,还有其他的实现方式,比如利用传说中更高性能的字节码操作机制,类似 ASM、cglib(基于 ASM)、Javassit 等。 很多繁琐重复的编程,都可以被动态代理机制优雅地解决。 3. 扩展 setAccessible我们使用反射调用 private 方法时,需要 setAccessible(true),setAccessible()可以理解成修饰成员的 public、protected、private,这意味着我们可以在运行时修改成员的访问限制。但 Java 9 以后,Jigsaw 项目新增的模块化系统,出于封装性的考虑,对反射访问进行了限制。Jigsaw 引入了所谓的 Open 概念,只有当被反射操作的模块和指定的包对反射调用者模块 Open,才能使用 setAccessible,否则认为不合法的操作。 动态代理首先它是一个代理机制,代理可以看作是对调用目标的一个包装,这样我们对目标代码的调用不是直接发生的,而是通过代理完成的。代理引入了中间层,我们可以在调用被代理对象前做一些其他操作,如诊断、限流。RPC 是一个很好的例子,框架内部的寻址、序列化、反序列化等,对于调用者往往是没有太大意义的,通过代理,可以提供更加友善的界面。 JDK Proxy 和 cglibSpring AOP 支持两种模式的动态代理,JDK Proxy 或者 cglib。 JDK Proxy 的优势JDK 本身的支持,依赖少,平滑进行 JDK 版本升级,代码实现简单 类似 cglib 框架优势没有实现同一接口限制,高性能 性能不是唯一考量,JDK Proxy 现在性能并不差,可靠性、可维护性、编程工作量等往往是主要考量因素。 动态代理的两个重要应用RPC 和 AOP 4. 总结没有反射就没有框架,使用反射就是为了提高代码灵活性。动态代理的两个典型应用 RPC 和 AOP,后续会再写相关文章。 5. 参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"创建一个spring boot 应用","date":"2018-09-15T07:22:23.000Z","path":"2018/09/15/创建一个spring-boot-应用/","text":"上一篇《Spring Boot 初识》介绍了 Spring Boot 特性,今天我们来创建一个 Spring Boot 应用,体验一下大火的 Spring Boot。 创建应用骨架 方式一 SPRING INITIALIZR 访问 https://start.spring.io/ 填写页面表单,注意 Search for dependencies 中我们输入 web 并选中候选项中的 web,最后点击 Generate Project,这时浏览器开始下载 xx.zip,这个 zip 就是网站根据我们填写的表单,为我们生成的一个 Spring Boot 应用的源代码压缩包。 解压缩 zip ,使用 idea 打开解压缩后的文件夹。 方式二 IDEA依次点击 File->New->Project 在左侧候选栏选择 Spring Initializer ,填写表单,依次点击 next 完成项目创建。 上面任意一种方式都可以完成项目的创建。 完成 Web 应用目前的项目还没有 Controller,让我们写个简单的 Controller,测试一下 1234567@RestControllerpublic class HelloController {@GetMapping(\"/hello\")public String helloSpringBoot() {return \"Hello Spring Boot!\";}} 运行测试启动 main 方法,我们的应用就启动了,观察控制台,我们可以看到这样的信息 1Tomcat started on port(s): 8080 (http) with context path '' 我们直接打开浏览器访问: 1http://localhost:8080/hello 可以看到,浏览器返回 1Hello Spring Boot! 到这里我们创建的第一个 Spring Boot web 应用就结束了。 观察一下应用骨架应用测试通过了,我们来看看这个自动生成的应用骨架 目录生成的是一个 maven 项目,有我们非常熟悉的 src 目录、pom.xml、.gitignore,另外有几个特殊的,它们是与 Maven wrapper 相关,让你在没有安装 maven 的情况下,可以运行 maven 。mvnw 用于 Linux 平台使用,mvnw.cmd 不用说,用于 Windows 平台。 pom.xml 123456789101112<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies> 上图就是我们创建的应用的依赖,就两个,没有其他了,很简洁,pom 中有我们创建项目时选的 web,可以看到这个依赖有点怪,没有版本,那它一定有 parent,来找找,果然如下 123456<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent> 去本地库看看这个项目只有一个 pom,没有 jar,看看里面的内容,竟然还有一个 parent 123456<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.0.5.RELEASE</version><relativePath>../../spring-boot-dependencies</relativePath></parent> 只能继续找了果然,版本控制都在这,我截图了一个 activemq,好了我们找到了 spring boot 的版本控制在 spring-boot-dependencies pom.xml 中。项目其它部分就没什么特殊的地方了。 总结我们学习了两种创建一个 Spring Boot 应用的方式,实际是一样的,了解了一下项目的基本结构,可以发现使用 Spring Boot 创建一个应用,是非常快的,真的是简化了 Spring 开发,我们没有配置任何一个 xml 文件,真的很爽,快来尝试一下吧。 参考文档What is the purpose of mvnw and mvnw.cmd files?","tags":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"http://freemeng.com/tags/Spring-Boot/"}]},{"title":"Spring Boot 初识","date":"2018-08-25T00:28:10.000Z","path":"2018/08/25/Spring-Boot-初识/","text":"本文为阅读《Spring 实战》中 Spring Boot 章节的学习笔记,初次接触 Spring Boot 对其不了解,担心书中内容关于 Spring Boot 部分比较旧,后续继续学习,发现有问题时我会更新内容,本文首发于我的公众号《ETLshow》。 1. Spring Boot 简介Spring 简化 Java 开发,Spring Boot 简化 Spring 开发。 2. 四个主要特性2.1 Spring Boot Starter 依赖Spring Boot Starter 依赖将所需常见依赖按组聚集在一起,形成单条依赖,减少了依赖列表的长度 Spring搭建 web 层时,pom.xml 中需要添加 spring-web、spring-mvc、jackson-databind 等多条依赖 SpringBoot搭建 web 层时,pom.xml 中仅需要添加 spring-boot-starter-web 原理:利用了 Maven 和 Gradle 的依赖传递特性 2.2 自动配置自动配置削减了 Spring 配置的数量,它在实现时,会考虑应用中的因素并推断你所需要的 Spring 配置 Spring搭建 web 层时,在 spring-mvc.xml 配置文件中,我们需要配置视图解析器、资源处理器、消息转换器等 Spring Boot搭建 web 层,无需配置 spring-mvc.xml,Spring Boot 自动配置 2.3 命令行接口 CLI结合 Groovy 进一步简化 Spring 开发。 Groovy 是一种基于 JVM(Java 虚拟机)的敏捷开发语言,它结合了 Python、Ruby 和 Smalltalk 的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。由于其运行在 JVM 上的特性,Groovy 可以使用其他 Java 语言编写的库。 2.4 Actuator 管理特性它开启了一扇窗,让我们洞悉应用的内部运行状况。主要功能就是为基于 SpringBoot 的应用添加多个有用的管理端点。为了启用 Actuator , pom.xml 中需要添加 spring-boot-starter-actuator 端点 /autoconfig 描述了 Spring Boot 在使用自动配置的时候,所做出的决策 /beans 列出运行应用所配置的 bean 等等 3. 总结Spring 简化 Java 开发,Spring Boot 简化 Spring 开发。 使用 starter 缩短了 pom 依赖,可以理解为对 Spring 小模块的封装。 使用自动配置,减少了 Spring 的使用配置。 配置少了,提供了管理端点来查看应用的信息。","tags":[{"name":"Spring Boot","slug":"Spring-Boot","permalink":"http://freemeng.com/tags/Spring-Boot/"}]},{"title":"程序员职业规划","date":"2018-08-05T10:51:58.000Z","path":"2018/08/05/程序员职业规划/","text":"今天在搜索职业规划时,阅读了下面这篇文章《程序员跳槽时,如何正确做好职业规划?》文章有点长,但我收藏了,推荐阅读,总结如下: 一、什么是职业规划?职业规划其实就是对职业生涯进行持续的,系统的计划。 二、为什么进行职业规划?有目标,才有方向。 三、职业规划的原则 1.是你偏爱的 2.是你擅长的 3.你可以实现价值 4.你可以长远发展 不要仅仅为了薪资或者地点或者公司知名度就选择一份新的工作,要根据以上四个原则,满足的越多,越匹配,到了中期以后才不会迷茫未来要何去何从。 四、如何增强自己的竞争力 1.拓宽业务场景多去尝试不同领域的公司,去了解不同领域的公司在技术实现上有什么异同,对于程序员更深入地理解技术是很有帮助的。 2.把握职场节奏在职场上有一个时间标准:3 年。 3 年时间,一定要在工作内容和职级上有一个质的变化,这个变化可以通过升职得到,也可以通过跳槽达成目标。 3.训练复合技能除了编程之外,程序员要尽量培养一些其他方面的技能,例如演讲、写作等等。 4.保持紧密沟通那些有意识在工作之余关注行业动态,积攒人脉资源,并且乐于与同行沟通的程序员,往往在职业发展的路上走得更远。 五、如何保持职场方向感程序员之间的竞争将会越来越激烈。 1.首先,你得有一个清晰的人生规划,才能有一个清晰的职业规划。 2.其次,互联网技术要有以万变胜不变的心态,时刻调整自己对自己的定位。 1)更新技术,踩上技术趋势 2)树立个人品牌 3)在团队的力量下,找到自己最大的价值六、总结“男怕入错行,女怕嫁错郎”,职业生涯,在漫漫人生路上是很重要的一部分,想想你要工作多少年?找个安静的夜晚,思考一下自己的人生规划,自己的职业生涯规划,制定阶段性目标,走出舒适区,生活不迷茫。 愿你的业余时间,不只有抖音、游戏、电影、电视剧","tags":[{"name":"随笔","slug":"随笔","permalink":"http://freemeng.com/tags/随笔/"}]},{"title":"走近 Java","date":"2018-07-28T06:45:28.000Z","path":"2018/07/28/走近Java/","text":"第 1 章 走近 Java 走近 Java ,了解 Java 技术体系、Java 发展史、Java 虚拟机发展史、Java 技术未来。声明:文章图片来源于网络 1. Java 技术体系 2. Java 发展史 3. Java 虚拟机发展史曾经涌现过太多虚拟机,或经典或优秀或有特色,记录几个特殊的。 世界第一款商用虚拟机:Sun ClassicSun JDK 和 OpenJDK 默认虚拟机及目前使用范围最广的虚拟机:HotSpot VM号称世界上运行最快的虚拟机:JRockit VM虚拟机 Dalvik VM 是 Andriod 平台的核心组成部分之一 4. Java 技术未来了解 Java 技术未来发展趋势,关注新版 JDK 提供的特性。 4.1 模块化原因:不希望为了系统中一小块的功能而不得不下载、安装部署及维护整套庞大的系统。进展:目前规范竞争中,一方为 Sun Jigsaw(拼图)另一方为 IBM OSGi。 4.2 混合语言原因:越来越复杂的软件,单一的 Java 语言已经无法很好满足,越来越多的基于 Java 虚拟机的语言被应用到软件项目中,Java 平台上的多语言混合编程正成为主流,每种语言都可以针对自己擅长的方面更好的解决问题。进展:出现了越来越多的基于 Java 虚拟机的语言,Java 虚拟机向多语言虚拟机发展。 4.3 多核并行原因:CPU 硬件发展方向由高频率转向为多核发展:JDK 1.5 java.util.concurrentJDK 1.7 java.util.concurrent.forkjoinJDK 1.8 LambdaOpenJDK 子项目 Sumatra,目前显卡的运算能力和并行能力已经远远超过了 CPU在 JDK 外围出现了 Hadoop MapReduce 一个简单易懂的并行框架 4.4 进一步丰富语法原因:好用才是王道,语法糖越来越多发展:OpenJDK coin 子项目 4.5 64位虚拟机原因:在 JavaEE 方面,企业级应用经常需要使用超过 4GB 的内存,对于 64 位虚拟机的需求是非常迫切的。目前 64 虚拟机内存耗用大,运行速度慢发展:由 32 位 向 64 位过渡中","tags":[{"name":"Java虚拟机","slug":"Java虚拟机","permalink":"http://freemeng.com/tags/Java虚拟机/"}]},{"title":"一个菜鸟程序员的反思","date":"2018-06-28T01:16:30.000Z","path":"2018/06/28/一个菜鸟程序员的反思/","text":"今天星期四,因为要去医院,所以倒休,没去上班。之前在网上看到这样一篇文章《优秀的程序员具备哪些属性?》遂今天反思一下。优秀程序员具有的特点: 1.兴趣“兴趣是最好的老师”,这。。。。。。可我偏偏不知道我的兴趣所在啊?然后,“干一行爱一行”,好。。。。。。 2.逻辑思维能力作为工科生,逻辑思维能力还好。什么?算法?那太难了。 3.记忆力“记忆力越来越不好了”,好记性不如烂笔头。但是记下了,终究不是自己的要有自己的理解。质量很重要。 4.自制力“我坚持一年多早晨 6 点起床”,这自制力还算可以。 5.运气顺其自然吧。 分析了,上面的几点,我发现,我真的是一个菜鸟程序员。除了上面这些点,还有一点就是自己的知识系统不完整,没有构成体系,很零散,导致长时间不用就完全遗忘。所以,我准备开始,整理自己的知识体系。并将以上几点,收录到公众号菜单,时刻提醒我自己。 欢迎关注我的微信公众号 ETLshow ,你不是一个人在努力。","tags":[{"name":"随笔","slug":"随笔","permalink":"http://freemeng.com/tags/随笔/"}]},{"title":"「第5讲」String、StringBuffer、StringBuilder 有什么区别?|学习笔记","date":"2018-06-16T09:45:13.000Z","path":"2018/06/16/「第5讲」String-StringBuffer-StringBuilder-有什么区别-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第 5 篇学习笔记,坚持写完 36 讲学习笔记。 1. 问题String、StringBuffer、StringBuilder 有什么区别? 2. 典型回答 StringString 是典型的 immutable(不可变的)类,被声明为 final Class 并且所有属性也都是 final 的,由于它的不可变性,类似拼接、截取字符串等操作,都会产生新的 String 对象。 StringBuffer为了解决 Spring 拼接等操作产生大量中间对象而提供的一个类。String Buffer 是线程安全的,但带来了额外的性能开销。 StringBuilderStringBuilder 在能力上与 StringBuffer 没有本质区别,但是它去掉了线程安全部分,有效减小了开销,是绝大部分情况下进行字符串拼接的首选。 一句话总结,String 不可变类,StringBuffer 线程安全,StringBuilder 线程不安全。 3. 扩展 1.StringBuffer 线程安全的实现方法?StringBuffer 的线程安全是通过把各种修改数据的方法都加上 synchronized 关键字实现的,非常简单粗暴。 2.StringBuffer 和 StringBuilder 是如何实现修改字符序列的?底层都是利用可修改的(char,JDK 9 以后是 byte)数组,二者都继承了 AbstractStringBuilder,里面包含了基本操作,区别仅在于最终的方法是否加了 synchronized。 3.这个内部数组应该创建多大呢?初始字符串长度加 16,如果我们确定拼接会发生非常多次,而且大概是可以预计的,那么就可以指定合适的大小,避免很多次扩容的开销。扩容会产生开销,因为要抛弃原有数组,创建新的数组,还要进行 arraycopy。 4.编译与反编译编译 1javac Demo.java 反编译1javap -v Demo.class 5.Dump Heapjava程序性能分析之thread dump和heap dump 6.字符串缓存jdk 6 以后提供了 intern()方法,目的是提示 JVM 把相应字符串缓存起来,以备重复使用。在我们创建字符串对象并调用 intern() 方法的时候,如果已经有缓存的字符串,就会返回缓存里的实例,否则将其缓存起来。JDK 8u20 以后,推出了新特性 G1 GC 下的字符串排重。它是通过将相同数据的字符串指向同一份数据来做到的,是 JVM 底层的改变,并不需要 Java 类库做什么修改。 4.总结一句话总结,String 不可变类,StringBuffer 线程安全,StringBuilder 线程不安全。 5.参考文档《Java 核心技术 36 讲》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第4讲」强引用、软引用、弱引用、幻象引用有什么区别|学习笔记","date":"2018-05-20T11:58:09.000Z","path":"2018/05/20/「第4讲」强引用、软引用、弱引用、幻象引用有什么区别?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第四篇学习笔记,坚持写完 36 讲学习笔记。 1. 问题强引用、软引用、弱引用、幻象引用有什么区别?具体使用场景是什么? 2. 回答2.1 强引用、软引用、弱引用、幻象引用有什么区别?在 JDK 1.2 之后,Java 对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Week Reference)和虚引用(幻象引用 Phantom Reference)4 种,这四种引用强度依次逐渐减弱。 强引用就是指程序代码中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。 软引用是用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。 弱引用也是用来描述非必须对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。 虚引用也称幻象引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。 2.2 具体使用场景是什么? 强引用程序中到处都是 软引用通常用来实现内存敏感的缓存 弱引用同样可用于内存敏感的缓存 虚引用可以来跟踪对象被垃圾回收器回收的活动 3. 拓展3.1 GC 需要完成的 3 件事情 哪些内存需要回收? 什么时候回收? 如何回收? 3.2 GC 回收的是哪一部分的内存?Java 堆和方法区 3.3 判断对象存活的方法 引用计数法Python 使用引用计数法进行内存管理,缺点无法解决对象间循环引用的问题 可达性分析算法Java 使用可达性分析算法进行内存管理 参考文档《Java 核心技术 36讲》《深入理解 Java 虚拟机》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第3讲」谈谈 final、finally、finalize 有什么不同|学习笔记?","date":"2018-05-20T01:44:43.000Z","path":"2018/05/20/「第3讲」谈谈-final、finally、finalize-有什么不同?-学习笔记/","text":"这是我学习极客时间 app 中《Java核心技术36讲》课程的第三篇学习笔记,目前 app 已经更新到第七讲,我得抓紧学习了,坚持写完 36 讲学习笔记。 都说程序员需要会英语,这个题,直接扔来三个单词,还长得这么像,什么不同?第一个形容词,第二个副词,第三个动词,哈哈,回答完毕。 典型回答 final 表示“这是无法改变的”,可以用来修饰类、方法、变量,final 修饰的类不可以被继承,final 修饰的方法不可以覆盖,final 修饰的变量不可以修改。 finally 常见于 try-finally 或者 try-catch-finally,是 Java 保证重点代码一定要执行的一种机制,可以用来类似关闭 JDBC 连接、保证 unlock 锁等动作。 finalize 是 java.lang.Object 的一个方法,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9 开始被被标记为 deprecated。 拓展 final对于基本类型,final 使数值恒定不变,而用于对象引用,final 使引用恒定不变。一旦引用被初始化指向一个对象,就无法把它改为指向另一个对象。只有在想明确禁止覆盖时,才将方法设置为 final 的。不要想着使用 final 方法来进行优化,因为现在虚拟机(特别是 hotspot 技术)已经进行了优化。2.finally当要把除内存之外的资源恢复到它们的初始状态时,就要用到 finally 子句。这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关。 参考文档《Java 编程思想》《Java 核心技术36讲》 推荐阅读在 JDK 9 中更简洁使用 try-with-resources 语句","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"「第2讲」Exception 和 Error 有什么区别|学习笔记","date":"2018-05-13T01:08:24.000Z","path":"2018/05/13/「第2讲」Exception-和-Error-有什么区别|学习笔记/","text":"这篇是极客时间中的一个课程《Java核心技术36讲》的学习笔记系列第二篇。 1. 题目《第二讲|Exception 和 Error 有什么区别?》 2. 问题问题:请对比 Exception 和 Error,另外,运行时异常与一般异常有什么区别? 请对比 Exception 和 ErrorException 和 Error 都继承了 Throwable 类。Exception 是程序正常 运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。 运行时异常与一般异常有什么区别?Exception 分为可检查(checked)异常和不检查(unchecked)异常。可检查异常在代码中必须显示进行捕获处理,这是编译期检查的一部分。不检查异常就是所谓运行时异常(run-time exception),通常是可以编码避免的逻辑错误,具体根据需要判断是否需要捕获,并不会在编译期强制要求。 3. 实践Java 代码开发过程中,异常处理不可避免,下面记录一些实践,仅供参考: idea 异常快捷键选中要进行 try catch 的代码片段,然后【Ctrl + Shift + T】,选择 try catch 操作,即可完成 try catch。 不要生吞异常 123try{}catch(SomeException e){} 生吞异常即,进行了 try catch 但是在 catch 中没有做任何处理,这是大忌,看似程序正常运行没有什么异常,实际异常已经发生了,但是被吞了。 正确记录异常12345try{// 业务代码}catch(IOException e){e.printStackTrace();} e.printStackTrace();是不允许的,我们必须使用 Logger 记录异常堆栈信息。 只对非安全代码进行 try catchtry catch 代码会产生额外的性能开销,它往往会影响 JVM 对代码进行优化,所以用在非安全代码上。 优先使用标准的异常有两个最常用的异常,一个是 IllegalArgumentException ,另一个是 IllegalStateException。 4. 总结这个第二讲介绍的是 Java 异常体系,以上是读书笔记,如有疑问或错误,欢迎留言指正。 5. 参考文档《Java 核心技术 36 讲》《Effective Java》","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"值得一看的 IDEA 快捷键说明","date":"2018-05-12T14:34:05.000Z","path":"2018/05/12/值得一看的-IDEA-快捷键说明/","text":"作为 JAVA 程序员,IDEA 是我最爱的 IDE,关于快捷键,网上文章一大堆,大部分都是罗列快捷键,给快捷键排名,非常不友好,遂有了写篇文章的想法。 1. 一个快捷键如果只记忆一个快捷键,那么就是 【Ctrl + Shift + A】即:Find Action。为什么呢?因为你可以通过它查找快捷键,再也不用担心不记得快捷键了。 2. 查看内置快捷键好多人去网络上收集快捷键列表,其实 IDEA 内置了,在哪里呢?不记得?来【Ctrl + Shift + A】然后输入 KR 即:Keymap Reference,如果顺利的话你就看到了,它长这个样子:IDEA 对快捷键进行了分类,很容易查看。 3. 强烈推荐的快捷键其实文章到这里就可以结束了,我觉得够了,但是,还是推荐几个我常用的快捷键吧,参考 IDEA 分类,这个列表我会适时更新。 Editing【Ctrl + Alt + T】 选择代码块 try catch【Alt + Insert】生成代码(如 get,set 方法,构造函数等)【Ctrl + W 】 选中代码,连续按 渐进选择 Usage Search Navigation【Ctrl + E】 最近打开的文件【Alt + F1】 查找代码所在位置【Ctrl + Alt + left/right】 返回至上次浏览的位置【Ctrl + B】 快速打开光标所在位置类,方法【Ctrl + Alt + B】 快速打开方法实现 Search/Replace Live Templates【Ctrl + J】 自动代码 Refactoring【Ctrl + Alt + L】 格式化代码【Ctrl + Alt + O】 优化导入的类和包 Debugging Compile and Run VCS/Local History General 4. 总结关于 IDEA 快捷键就写到这里了,你有什么推荐的快捷键?欢迎留言分享。","tags":[{"name":"IDE","slug":"IDE","permalink":"http://freemeng.com/tags/IDE/"}]},{"title":"「第1讲」谈谈你对 Java 平台的理解|学习笔记","date":"2018-05-07T23:52:15.000Z","path":"2018/05/08/「第1讲」谈谈你对Java平台的理解-学习笔记/","text":"最近朋友圈及公众号被极客时间中的一个课程《Java核心技术36讲》广告刷屏了,加上最近想夯实一下 Java 基础,所以购买了该课程。为了不浪费我的大洋,并提高学习效果,准备写系列文章,记录学习笔记,希望可以坚持下去。第一讲|谈谈你对 Java 平台的理解? 问题:谈谈你对 Java 平台的理解?“Java 是解释执行”,这句话对么? 好,这是两个问题,我们把它拆解开,分开学习。 谈谈你对 Java 平台的理解?先放一张 Java 技术体系图根据作者提供的思维导图,重新绘制了一下看了这张大图,好吧,我继续努力。 Java 是解释执行?首先,我们来学习一下解释执行与编译执行。解释执行与编译执行,是计算机编程语言的两种执行方式。 编译执行编译器将高级语言(如 C)源程序作为输入,进行翻译转换,产生出机器语言的目标程序,然后再让计算机去执行这个目标程序,得到计算结果。虽然编译过程本身较为复杂,但一旦形成目标文件,以后可多次使用。 解释执行解释器将源语言(如 BASIC)书写的源程序作为输入,解释一句后就提交计算机执行一句,并不形成目标程序。解释程序执行速度很慢,例如源程序中出现循环,则解释程序也重复地解释并提交执行这一组语句,这就造成很大浪费。 我们开发的 Java 的源代码,首先通过 Javac 编译(这里说的编译和 C/C++ 是有着不同意义的,Javac 的编译生成的不是可以直接执行的机器码)成为字节码。然后,在运行时,通过 Java 虚拟机(JVM)内嵌的解释器解释执行。但是常见的 JVM,比如我们大多数情况使用的 Oracle JDK 提供的 Hospot JVM,都提供了 JIT(Just-In-Time)编译器,也就是通常所说的动态编译器,JIT 能够在运行时将热点代码编译成机器码,这种情况下部分热点代码就属于编译执行,而不是解释执行了。 举例:JDK 8 实际是解释和编译混合的一种模式,即所谓的混合模式(-Xmixed)。Java 虚拟机启动时,可以指定不同的参数对运行模式进行选择。 比如,指定“-Xint”,就是告诉 JVM 只进行解释执行。指定“-Xcomp”,就是告诉 JVM 只进行编译执行。除了我们日常最常见的 Java 编译模式,其实还有一种新的编译方式,即所谓的 AOT(Ahead-of-Time Compilation),直接将字节码编译成机器代码,这样就避免了 JIT 预热等各方面的开销,比如 Oracle JDK 9 就引入了实验性的 AOT 特性。 推荐阅读Java 三种编译方式:前端编译 JIT 编译 AOT 编译解释性语言和编译性语言的区别 参考文档编译执行和解释执行的区别","tags":[{"name":"Java","slug":"Java","permalink":"http://freemeng.com/tags/Java/"}]},{"title":"时间管理--如何充分利用你的24小时读书笔记","date":"2018-04-21T10:15:25.000Z","path":"2018/04/21/时间管理/","text":"最近在读吉姆·兰德尔的《时间管理-如何充分利用你的24小时》,目前还没有读完,但有一些收获记录一下。那就是: 一定要确立目标,让你的时间去服务你的目标,这样就是充分利用你的 24 小时。没有目标,你就不知道如何规划时间,你那每天仅有的可自由支配的时间就会浪费掉。想了想,生活其实很简单,就两条路:第一条,你可以随波逐流,任由生活摆布,没有方向,生活让你痛苦,你就痛苦,生活让你快乐,你就快乐!第二条,你可以有目标,有理想,每天充分利用你的时间,在实现理想,达成目标的路上,痛并快乐着! 愿你意识到自己的生活,选择一条路","tags":[{"name":"随笔","slug":"随笔","permalink":"http://freemeng.com/tags/随笔/"}]},{"title":"笔记本选购学习笔记","date":"2018-04-15T01:18:28.000Z","path":"2018/04/15/笔记本选购学习笔记/","text":"由于最近要购买笔记本,所以学习了解一下笔记本选购,如下为学习笔记。 1.部件选择 CPU选择指标顺序 1234U :低电压,功耗低,常见于轻薄本HQ :(不可拆卸&四核)常见于游戏本,功耗大,发热大,对应散热需求大关于系列的选择,低电压版 CPU i7 相对于 i5 提高不多,但价格相差 500 左右,i5 系列的 U 是更好的选择。HQ i7 相对于 i5 多核性能提升较多,沙盘类游戏,地图较大,比较吃 CPU ,如果有需要可以考虑 i7 系列 HQ。第一人称类游戏,画面渲染需求高,主要看显卡,CPU 看单核性能就可以,单核性能,i5 i7 差别不大,上图为多核性能比较。总得来说,i5 i7 游戏体验差不多。 显卡 GPU游戏本要重点选择显卡。显卡分 N 卡和 A 卡,A 卡就不说了,用的少。选择方法说明:有 GTX 的没有的强,轻薄本常用 MX150 ,后面两个数字最重要。1060 要比 1050 强 2 倍,一般轻薄本在 50 以下,游戏本,在 50 以上。Ti 表示加强版,1050 Ti 性能介于 1050 与 1060 之间。 屏幕屏幕大小:常见 13 15 寸,轻薄本多 13,游戏多 15。屏幕技术:TN 不要考虑了, IPS 较好。色域:72%NTSC 与 45%NTSC 差距还是挺大的。分辨率:1080P 即可,2K 更细腻,但看电影无用,windows 优化不好。触屏:没必要。 内存(memory) 主要看大小和速度,内存速度并不是电脑短板。大小主要看需求,通常 8G,而且需要可以扩展。 硬盘(storage)硬盘是电脑木桶原理的最短板,开机速度,解压缩速度都取决于硬盘。硬盘速度很重要。PCI-E SSD 要好于 SATA SSD。 接口最需要注意的是雷电 3 接口,速度有 40 Gbps,可以代替一切接口,苹果就是这么做的,未来趋势。 2.笔记本比较 2011 年笔记本 Sumsung(RC410) 2016 年笔记本 联想 Y430P 2017 年笔记本 hp 440 2018 年笔记本 联想 T470S 3.性价比区间 轻薄本 5000-6000 游戏本 7000-9000 苹果本 10000-30000 4.我的备选笔记本 1.联想 ideapad720s 15(全能本)12345678i5-7300HQ 处理器GTX1050ti 4GB 独立显卡(MAX-Q)8GB 内存256GB 固态硬盘15.6 寸 1080P 分辨率 72%NTSC 色域 IPS 屏厚 17.9mm重 1.9kg目前售价 7299 元 优点!1,维持商务风格的同时,具有较强影音游戏性能2,79Wh电池容量大,续航时间长3,搭载最新三星PM981固态硬盘,且内部支持双内存+双NVMe固态,扩展性强缺点!1,7代i5仅四核四线程,规格过时2,由于轻薄设计,散热表现不如传统游戏本3,虽有雷电3接口,但仅有x2带宽 2.macbook pro13寸 8G 256G 不带 touch bar 版 2018年4月14日京东价:11488 元 天猫价:11888 元 5.参考文档十几分钟,教会你如何购买笔记本电脑【bonjour呼呼】","tags":[{"name":"随笔","slug":"随笔","permalink":"http://freemeng.com/tags/随笔/"}]},{"title":"docker初识","date":"2018-04-14T03:22:38.000Z","path":"2018/04/14/docker初识/","text":"听说 Docker 好久了,昨天听同事说,我们测试环境的 linux 都不是物理机,都是 Docker,记得之前微信收藏了一篇介绍 Docker 的文章,便翻出来看了一下,有输入就要有输出,所以写篇小文,记下基本知识,本文只涉及 Docker 介绍,如果想详细了解,请阅读参考文章。 一.啥是 Docker 虚拟机 -> Linux 容器 -> Docker 虚拟机(virtual machine) 就是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。 Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。有点像轻量级的虚拟机,能够提供虚拟化的环境,但是成本开销小得多。 Docker Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。 二.为啥使用 Docker1.合作开发的时候,在本机可以跑,别人的电脑跑不起来。- 崩溃2.服务器自己的程序挂了,结果发现是别人程序出了问题把内存吃完了,自己程序因为内存不够就挂了。- 倒霉3.公司要弄一个活动,可能会有大量的流量进来,公司需要再多部署几十台服务器。- 搞死 三.Docker 用途Docker 的主要用途,目前有三大类。 (1)提供一次性的环境。比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。(2)提供弹性的云服务。因为 Docker 容器可以随开随关,很适合动态扩容和缩容。(3)组建微服务架构。通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。 三.基本概念 镜像Docker 把应用程序及其依赖,打包在 image 文件里面 仓库存放 image 容器运行 image四.参考文章 1.Docker 入门,看这篇就够了2.Docker 入门教程3.Docker 微服务教程","tags":[{"name":"Docker","slug":"Docker","permalink":"http://freemeng.com/tags/Docker/"}]},{"title":"hadoop java heap space","date":"2018-04-13T14:44:38.000Z","path":"2018/04/13/hadoop-java-heap-space/","text":"1.问题描述在 hdoop 集群执行 job 时报错,命令如下:1hadoop jar xxx-job-1.0.0.jar xxxx \"2018-04-02\" \"/d1/d2\" 错误信息:1218/04/12 09:13:06 INFO mapreduce.Job: Task Id : attempt_1523454594132_0002_m_000002_0, Status : FAILEDError: Java heap space 2.原因分析错误信息较少,但可以看出内存溢出了,下面开始排查问题:1.确认 hadoop 版本12hadoop versionHadoop 2.7.2 2.确认是 map 阶段,还是 reduce 阶段发生异常http://[nameNodeServer]:8088/cluster 我的是 map 阶段内存溢出 3.解决方法查询官方文档,更改内存配置http://hadoop.apache.org/docs/r2.6.5/hadoop-yarn/hadoop-yarn-common/yarn-default.xmlhttp://hadoop.apache.org/docs/r2.6.5/hadoop-mapreduce-client/hadoop-mapreduce-client-core/mapred-default.xml 第一个配置文件 mapred-site.xml 123456789101112<property><name>mapred.child.java.opts</name><value>-Xmx6144m</value></property><property><name>mapreduce.map.memory.mb</name><value>4096</value></property><property><name>mapreduce.reduce.memory.mb</name><value>4096</value></property> mapred.child.java.opts 定义 mapreduce 执行的 container 容器的执行 jvm 参数 mapreduce.map.memory.mb 单独指定 map 阶段的执行 jvm 参数 mapreduce.reduce.memory.mb 单独指定 reduce 阶段的执行 jvm 参数map 阶段:mapreduce.admin.map.child.java.opts < mapred.child.java.opts < mapred.map.child.java.opts, 也就是说最终会采用 mapred.map.child.java.opts 定义的 jvm 参数,如果有冲突的话。 第二个配置文件 yarn-site.xml 1234<property><name>yarn.nodemanager.vmem-pmem-ratio</name><value>8</value></property> yarn.nodemanager.vmem-pmem-ratio 虚拟内存与物理内存比率 参考文档Hadoop作业JVM堆大小设置优化Hadoop Mapreduce Error: GC overhead limit exceeded","tags":[{"name":"hadoop","slug":"hadoop","permalink":"http://freemeng.com/tags/hadoop/"}]},{"title":"Day01 hadoop 初识","date":"2018-04-12T14:16:44.000Z","path":"2018/04/12/Day01-hadoop-初识/","text":"0. 学习目标123第一天接触具体的大数据框架,总目标是让学习者建立起大数据和分布式的宏观概念1.理解 hadoop 是什么,用于做什么,大体上怎么用2.通过一个案例的演示说明,理解数据挖掘系统的基本流程和结构 1. hadoop 背景介绍 什么是 hadoop ? 1.hadoop 是 apache 旗下的一套开源软件平台 2.hadoop 提供的功能:利用服务器集群,根据用户的自定义业务逻辑,对海量数据进行分布式处理 3.hadoop 的核心组件有 1.HDFS(分布式文件系统) 2.YARN(运算资源调度系统) 3.MAPREDUCE(分布式运算编程框架) 4.广义上来说,hadoop 通常是指一个更广泛的概念—— hadoop 生态圈 hadoop 应用案例 1.用户画像(百度大数据) 2.网站点击流日志数据挖掘(谷歌分析、友盟统计) hadoop 生态圈及各组成部分简介重点组件:HDFS:分布式文件系统MAPREDUCE:分布式运算程序开发框架Oozie:工作流调度框架HIVE:基于大数据技术(文件系统+运算框架)的 SQL 数据仓库工具HBASE:基于 HADOOP 的分布式海量数据库ZOOKEEPER:分布式协调服务基础组件Mahout:基于 mapreduce/spark/flink 等分布式运算框架的机器学习算法库Sqoop:数据导入导出工具Flume:日志数据采集框架 2. 离线数据分析流程介绍(网站点击流日志数据挖掘系统)","tags":[{"name":"hadoop","slug":"hadoop","permalink":"http://freemeng.com/tags/hadoop/"}]},{"title":"Python Demo(定投基金小工具)优化一","date":"2018-04-02T14:22:26.000Z","path":"2018/04/02/Python-Demo-定投基金小工具-优化一/","text":"第一版实在简陋,赶紧完善一下,闲话少说,上代码。 123456789101112131415from salary_increase import count_moneywhile True: print(''' 定投十年翻十倍! 开始定投吧! 0.个人加薪计划, 1.退出;''') option = int(input('请选择你的定投计划:\\n')) if option == 0: pe = float(input('请输入市盈率(PE):\\n')) result = count_money(pe) print('''经计算,本期应投入金额为: %d 元!''' % result) else: break 优化内容1.增加循环,进行多次计算;2.增加退出功能;3.由于 PE 多为小数,即浮点数,类型转换由 int 改为 float ;4.对输出结果进行格式化。 效果图 源码地址 https://github.com/zmdstr/xueqiu","tags":[{"name":"python","slug":"python","permalink":"http://freemeng.com/tags/python/"}]},{"title":"Python Demo(定投基金小工具)","date":"2018-04-02T13:39:13.000Z","path":"2018/04/02/Python-Demo-定投基金小工具/","text":"学习 python 有一段时间了,因为工作不使用 python,后来就中断学习了,最近在看有关基金定投的书籍,尝试用 python 写一下相关的工具,顺便复习一下 python,希望通过实际应用,可以继续学习 python。作为 python 初学者,文章难免有疏漏,一个人自学实在不易,欢迎留言指正,共同进步! 1.功能在定期不定额进行定投基金时,需要根据当前是第几个月定投和当前定投基金的市盈率(PE)计算投入金额。 2.代码实现xueqiu.py 12345678910111213from salary_increase import count_moneyprint('''定投十年翻十倍!开始定投吧!请选择你的定投计划:0.个人加薪计划;1.待创建;''')option = int(input())if option == 0: pe = int(input('请输入 PE ')) result = count_money(pe) print('''经计算,本期应投入金额为''' + str(result))else: print('待开发!') salary_increase.py 1234567891011from datetime import datedef count_money(pe): begin_year = date(2018, 3, 17).year begin_month = date(2018, 3, 17).month today_year = date.today().year today_month = date.today().month num = (begin_year-today_year)*12 + begin_month - today_month result = 1000*1.01**num*(10/pe) return result 3. 运行效果 4.知识点梳理 输出 用 print() 在括号中加上字符串,就可以向屏幕上输出指定的文字。‘’’ ‘’’ 为多行字符串 输入 用 input() 来获取用户输入,注意获得的是字符串,需要使用 int() 进行类型转换 条件判断 注意 python 使用缩进来定义代码块,而 java 使用 {} 定义函数 python 使用 def 定义一个函数,缩进的内容为函数体 datetime 模块 在计算当前月,是第几个月时,引入 datetime 模块中的 date 模块 导入模块 datetime 为内置模块 5. 源码地址https://github.com/zmdstr/xueqiu 6. 总结这是一个很简单的 python Demo,功能还不完善,后续会继续完善!","tags":[{"name":"python","slug":"python","permalink":"http://freemeng.com/tags/python/"}]},{"title":"关于目标","date":"2018-03-11T11:24:59.000Z","path":"2018/03/11/关于目标/","text":"I have a dream ! 最近在看一本理财方面的书籍《小狗钱钱》,感触颇多,记录一下。 1. 目标与理想记得小的时候,我们在快要毕业的时候,会买一本同学录,发给我们的同学,让同学们留下一些自己的介绍,以及对我们未来的祝福。我记得里面有一项叫理想。 “成为一名出色的科学家!” “当一名医生!” “考上一个好大学!” …… 这是我记忆中的理想,现在想想,当时其实只是想了想以后想成为一个什么样的人,就填了那么一项。结果呢?估计大多数人,那时填下的理想都随着时间远去了吧。但一定有坚持的人,他们一定都实现了自己的理想,或在实现自己理想的路上。 人活着没有理想,那与行尸走肉又有什么区别? 人生需要理想,那是我们活着的意义,一个企业也应该有自己的愿景,那是一个企业发展壮大的原动力。 我觉得目标就是小理想,是在我们实现自己理想的过程中,每一个阶段的小理想。 2. 制定自己的目标周末了,不知道干什么,打打游戏,刷刷头条,看看小说,觉得生活没意义,不充实,那么我觉得你离行尸走肉不远了。没有目标,你当然不觉得时间的珍贵了,就会肆意挥霍,而不是所谓的放松。 我的目标是什么呢?其实,很简单,提升自己的方方面面啊。 我想加薪,那么去看一下,目前阻碍你加薪的是什么?技术哪里有短板?定一个目标,今年我要加薪 30% 。 我想父母开心,利用业余时间,学几个拿手菜,过年回家时,露一手。定一个目标,今年我要让父母非常开心。 我想 ….. ,定一个目标,…… 3. 制定实现目标的计划有了目标,就像我们玩吃鸡一样,目标就是得第一,顺利吃鸡,接下来就是和队友配合,不断向目标靠近。 从哪跳伞?选择什么路线?这都是实现目标的计划。 同样的道理,我们必须制定实现自己目标的计划,然后按照计划去实施,一步一步去靠近我们的目标。 4. 不断想象目标实现时的场景实现目标的路上是充满诱惑的,如何坚持下去? 梦想画册是《小狗钱钱》一书中提到的,就是为自己的梦想准备一个画册,贴满与你梦想相关的照片。 你想拥有一个大 house ,那么就去找一个漂亮的房子的照片。你想脱单,就去找一个令人羡慕的情侣图。然后呢?你觉得很闲的时候,就去看一看,憧憬一下梦想实现时的样子。你会有很强的动力,这时你就可以坚持执行计划了。想想你目标实现时的喜悦,你身处一个属于你的大 house 中,阳光撒在你的脸上,暖洋洋的…… 经常想象目标实现时的场景,会我们坚持下去的动力,使我们能坚定不移的执行计划,不断靠近目标。 5. 愿你成功有了目标,生活有了方向,不再迷茫,我们清楚现在做的一切是为了什么,并且,深知这一切是值得的,因为我们很清楚,在目标实现的那一刻,那种感觉是多么美妙的。 愿你成功!","tags":[{"name":"随笔","slug":"随笔","permalink":"http://freemeng.com/tags/随笔/"}]},{"title":"一次 redis 连接超时排查","date":"2017-10-14T13:06:01.000Z","path":"2017/10/14/一次-redis-连接超时排查/","text":"errorredis 连接超时1Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out 排查过程由于是突然发生,代码没有任何改变,所以没有去检查代码及 redis 连接的配置。1.现象开发环境(我的本地)连接超时,但是测试环境(云环境 redis 安装环境)连接的是同一个 redis,代码都一样,连接正常。开发环境使用 rediscli 客户端,连接超时。2.telnet 测试开发环境 telnet redis 所在服务器端口,无法建立连接测试环境 telnet redis 所在服务器端口,可以正常建立连接初步确认应该是防火墙问题。我们的服务器是在某云上的,先去云上看了一下防火墙端口设置,没问题。然后去看了物理机 CentOs 系统的防火墙也没问题。突发奇想,使用开发环境连接手机热点,telnet OK,启动项目连接正常,难道,公司网络部做了限制。。。 解决方法经确定,公司网络部对端口进行了限制,所以更改了服务器上 redis 的端口,解决了此问题。 总结我这个情况是突发,如果你的 redis 刚搭建,配置完就报连接超时的错误,要去看看 redis 配置文件中的下面两个配置: 12#bind 127.0.0.1 #绑定 ipprotected-mode no #保护模式","tags":[{"name":"redis","slug":"redis","permalink":"http://freemeng.com/tags/redis/"}]},{"title":"使用 idea debug 远程 tomcat","date":"2017-08-19T00:45:43.000Z","path":"2017/08/19/使用-idea-debug-远程-tomcat/","text":"操作步骤 配置 idea 在 idea 中点击 Edit Configuration 配置一个 remote server主要配置项 Host服务器外网 ip Port端口号配置 OK 后,idea 会生成一个配置,显示在 Command line arguments for running remote JVM 下方1-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 配置服务器 tomcat 进入服务器 tomcat 下的 bin 目录编辑 catalina.sh 文件,修改 CATALINA_OPTS,添加上面 idea 给出的配置 1-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 编辑后完整配置如下: 12CATALINA_OPTS=\"-Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -DSERVERID=207 -DRuntimeEnv=prd \"JAVA_OPTS=\"-server -Xms2048m -Xmx2048m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000\" 启动服务器 tomcat idea 中选择远程 remote 启动 debug 从 idea 的 Console 中可以看到如下信息: 1Connected to the target VM, address: '120.92.xx.xx:8000', transport: 'socket' 设置断点,进行调试 idea 中停止调试 点击调试停止按钮,从 idea 的 Console 中可以看到如下信息:1Disconnected from the target VM, address: '120.92.xx.xx:8000', transport: 'socket' debug 远程 tomcat 结束 注意事项 8000 端口是我们设置的调试的端口,可以更改,需要注意防火墙开放该端口的访问; 注意 8000 端口不要被占用了,不同的 tomcat 应该配置不同的 debug 端口。 拓展阅读java 调试原理","tags":[{"name":"tomcat","slug":"tomcat","permalink":"http://freemeng.com/tags/tomcat/"}]},{"title":"Java不仅仅是一门编程语言","date":"2017-06-26T14:28:03.000Z","path":"2017/06/26/Java不仅仅是一门编程语言/","text":"Java 不仅仅是一门编程语言,它是一个技术体系。一次编写,到处运行。 Java 技术体系主要由以下几部分组成: java 程序设计语言 各种硬件平台上的 java 虚拟机 Java API类库 来自商业机构和开源社区的第三方Java类库 一般情况下,一个程序员只要了解了必要的Java API、Java语法,以及学习适当的第三方开发框架,就已经基本能满足开发的需要了。 体系图: Java 与 C 的区别Java 走的是二进制跨平台路线,发布的是“跨平台”的二进制可执行文件(Java Class 文件),然后依赖每个平台上实现的 JVM 来执行之;C 走的是源码跨平台路线,发布的是“源码”,在每个平台上重新编译一遍就可以使用,两者的取舍有所不同。参考 https://www.zhihu.com/question/30674148C 偏底层,Java 应用更广,Java 虚拟机是使用 C 语言开发的。 java 虚拟机Oracle 目前取得了三大商业虚拟机的其中两个:JRockit 和 HotSpot。HotSpot 是 SunJDK 和 OpenJDK 中所带的虚拟机,也是目前使用范围最广的 Java 虚拟机。JRockit 曾经号称“世界上速度最快的Java虚拟机”。","tags":[{"name":"java虚拟机","slug":"java虚拟机","permalink":"http://freemeng.com/tags/java虚拟机/"}]},{"title":"2016年流行的UI框架","date":"2017-06-25T08:42:37.000Z","path":"2017/06/25/2016年流行的UI框架/","text":"最近,一直在做移动端 web 开发,记录一下使用的前端框架。目前使用过两个,一个是 Amaze UI,一个是 WeUI 。 UI 是什么意思 ?这是百度百科的定义:UI 即 User Interface( 用户界面)的简称。泛指用户的操作界面,UI 设计主要指界面的样式,美观程度。而使用上,对软件的人机交互、操作逻辑、界面美观的整体设计则是同样重要的另一个门道。好的 UI 不仅是让软件变得有个性有品味,还要让软件的操作变得舒适、简单、自由,充分体现软件的定位和特点。 UI 框架又是什么意思 ?UI 是用户界面的英文简称,那么 UI 框架又是什么呢?框架又可以理解为模板,UI 框架也就是 UI 模板。这和编程里的封装有些类似,就是说一些 UI 设计的常用效果已经被别人封装成了 UI 框架,你想实现哪个效果只要直接调用就行,不需要太纠结于底层实现。例如:一个提示框、一个轮播图。以下转自知乎,地址如下: https://zhuanlan.zhihu.com/p/22707913 ,感谢原作者!目前,众多互联网公司 APP 都嵌入了大量的 HTML5,移动端的开发越来越重视,HTML5 的运用场景也越来越多了。在移动 Web 开发的过程中,使用合适的移动 WEB UI 框架可以大大提升我们的开发效率。下面就把 2016 年最流行的移动 Web 前端 UI 框架分享给大家。 Amaze UIAmaze UI 是一个轻量级(所有CSS和JS gzip后100kB左右)、Mobile first 的前端框架, 基于开源社区流行前端框架编写。Amaze UI Github 地址:GitHub - amazeui/amazeui: Amaze UI, a mobile-first and modular front-end framework.Amaze UI官网:Amaze UI | 中国首个开源 HTML5 跨屏前端框架 Frozen UIFrozen UI 是一个简单易用,轻量快捷,为移动端服务的前端框架,专注于移动 web 的 UI 框架,基于腾讯手机 QQ 规范。Frozen UI Github 地址:FrozenUI - 专注于移动 web 的 UI 框架 SUISUISUISUI 是一套基于 bootstrap 开发的前端组件库,同时她也是一套设计规范。通过 SUI,可以非常方便的设计和实现精美的页面。SUI 官网:http://sui.taobao.org/ ZUIZUIZUIZUI 是一个开源前端实践方案,帮助你快速构现代跨屏应用。ZUI Github 地址:GitHub - easysoft/zui: ZUI is an HTML5 front UI framework. H-uiH-uiH-uiH-ui 前端框架是在 bootstrap 的思想基础上基于 HTML、CSS、JAVASCRIPT 开发的轻量级 web 前端框架,开源免费,简单灵活,兼容性好,满足大多数中国网站。H-ui Github 地址:jackying (辉哥) · GitHubpure.csspure.css 一组小,响应 CSS 模块,您可以使用在每一个 web 项目pure.css Github 地址:GitHub - yahoo/pure: A set of small, responsive CSS modules that you can use in every web project. UIkitUIkitUIkitUIkit 是 YOOtheme 团队开发的一款轻量级、模块化的前端框架,可快速构建强大的前端 web 界面。UIKit 使用的变量基于 LESS,具有体积小、模块化、可轻松地自定义主题及响应式设计可在多种环境中使用等特点。UIkit 中文网为广大国内开发者提供详尽的中文文档、代码实例等,帮助开发者快速掌握并使用这一框架。UIkit Github地址:GitHub - uikit/uikit: A lightweight and modular front-end framework for developing fast and powerful web interfaces. BootstrapBootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS/HTML 框架。目前,Bootstrap 最新版本为3.3.7 。Bootstrap 中文网致力于为广大国内开发者提供详尽的中文文档、代码实例等,助力开发者掌握并使用这一框架。Bootstrap 中文网:Bootstrap中文网 拼图拼图号称中国版的 Bootstrap,优秀的跨屏响应式布局前端开发框架(CSS框架),国内前端框架先驱及领导者,能自动适应电脑、平板、手机等设备,让 web 前端开发更简单、快速、便捷。拼图官网:http://www.pintuer.com/ Plane UIPlane UI 是一个构建 HTML5 应用的跨终端响应式前端界面框架及解决方案。Plane UI Github地址:Plane UI - HTML5 跨终端响应式前端界面框架 FoundationFoundation 5 是我们最快、最好的构建。多用的功能来帮助编写代码和学习比以往任何时候都要快。Foundation 官网:Foundation | The most advanced responsive front-end framework in the world. muimui 是最接近原生 APP 体验的高性能框架。mui Github 地址:GitHub - dcloudio/mui: 最接近原生 APP 体验的高性能框架 WeUIWeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信 Web 开发量身设计,可以令用户的使用感知更加统一。包含button、cell、dialog、 progress、 toast、article、icon等各式元素。WeUI Github 地址:GitHub - weui/weui: A UI library by WeChat official design team, includes the most useful widgets/modules in mobile web applications. AraleArale 立足于支付宝的前端需求和国内前端社区,基于 Sea.js 和 spm 生态圈,致力发展小而美的前端模块架构,建立了一套从编码测试到部署的开发体系, 是一个开放、简单、易用的前端解决方案。Arale 模块均兼容于 IE6+ 以及 Chrome/Firefox/Safari/Opera 的最新稳定版。Arale 官网:http://aralejs.org/ Semantic UISemantic UI 是一款语义化设计的前端开源框架,其功能强大,使用简单,为设计师和开发师提供可复用的完美设计方案。Semantic UI Github 地址:GitHub - Semantic-Org/Semantic-UI-pt-br: Semantic dá poder a designers e desenvolvedores através da criação de um vocabulário comum para UI Jingle UIJingle UI 是一个基于 html5、css3 开发轻量级的移动 webapp 框架,提供一些基本交互方式,常用的组件(scroll,actionsheet,sidemenu,toggle,push2refresh……),帮助您更方便的开发移动应用。Jingle UI Github 地址:GitHub - shixy/Jingle: Jingle UI是一个基于html5、css3 开发轻量级的移动 webapp 框架,提供一些基本交互方式,常用的组件(scroll,actionsheet,sidemenu,toggle,push2refresh……),帮助您更方便的开发移动应用。 CMUICMUI 是一个专攻移动网页的 UI 框架,它提供了丰富的组件和简洁的接口,开箱即用。CMUI 帮助开发者摆脱样式细节和兼容性困扰,从而腾出更多精力投入到业务开发中。CMUI Github 地址:GitHub - CMUI/CMUI: Lightweight UI solution for mobile web.","tags":[{"name":"web","slug":"web","permalink":"http://freemeng.com/tags/web/"}]},{"title":"孔雀鱼","date":"2017-06-25T03:02:21.000Z","path":"2017/06/25/孔雀鱼/","text":"孔雀鱼(学名:Poecilia reticulata),又名孔雀花鳉、虹鳉,是一种常见小型热带观赏鱼,颜色鲜艳,品系多,很受水族爱好者欢迎。 分布及生活环境原产于南美洲的委内瑞拉、巴巴多斯、特立尼达、巴西北部与圭亚那,喜欢12至26度的水温,普遍是生活在硬水,酸碱值偏弱碱性(pH7至7.5)的环境。 特征孔雀鱼活泼好动,食性广泛,喜食鱼虫等动物类食物,繁殖力强,有人因此称其为“百万鱼”,喜欢群居活动。 繁殖孔雀鱼的繁殖周期相当短,从幼鱼到具繁殖能力的成鱼仅需2至3个月。每月繁殖一次。孔雀鱼会吃食同伴所生之幼鱼,甚至连自己所生之幼鱼亦同,故培育时需特别注意怀孕的母鱼,适时隔离于阴暗处,并放于水草缸中待产,可获得最高的生产存活率及仔鱼收获率。孔雀鱼是卵胎生的鱼类一胎约可生数十尾幼鱼,从母鱼的年龄、体型、健康情形来判断,初次生产的母鱼通常只会生10至20尾左右,如是体长超过5公分的成熟母鱼,生上百只是不成问题的。孔雀鱼的养殖上,可以加入少许盐类以减少孔雀鱼的疾病感染,例如水霉病等发生。 我的孔雀鱼 参考文章维基百科","tags":[{"name":"随笔","slug":"随笔","permalink":"http://freemeng.com/tags/随笔/"}]},{"title":"时间戳转_Date_字符串出现误差","date":"2017-06-24T09:44:18.000Z","path":"2017/06/24/时间戳转-Date-字符串出现误差/","text":"1. 问题描述: 前台一个日期选择组件,提交的数据格式为“1991-05-10”,后台使用 SimpleDateFormat 进行转换,获取到时间戳,存入数据库,数据库字段为 bigint 类型,保存后,日期回显,显示为 “1991-05-09”,出现一天的误差,但不是所有日期都存在误差。 前台获取到时间戳,转换后调用 toLocaleDateString 回显数据。 2. 解决方法: 后台,在使用 SimpleDateFormat 时,设置时区,如下: 1simpleDateFormat.setTimeZone(TimeZone.getTimeZone(\"UTC\")); 3. 相关代码如下: 1234567891011SimpleDateFormat simpleDateFormat = new SimpleDateFormat(\"yyyy-MM-dd\"); simpleDateFormat.setTimeZone(TimeZone.getTimeZone(\"UTC\")); Date birthdayDate = null; try { birthdayDate = simpleDateFormat.parse(birthday); long time = birthdayDate.getTime(); long l = time / 1000; member.setBirthday(l); } catch (ParseException e) { LOGGER.error(\"出生日期转换异常\", e); } 12345678910<script>// 变换日期 Date.prototype.toLocaleDateString = function () { return this.getFullYear() + \"-\" + (this.getMonth() + 1) + \"-\" + this.getDate(); }; var date_before = $(\"#birthday_source\").val(); var unixTimestamp = new Date(date_before * 1000); var new_date = unixTimestamp.toLocaleDateString(); $(\"#birthday_input\").val(new_date);</script> 1<input id=\"birthday_input\" name=\"birthday\" value=\"\" onchange=\"save()\" style=\"text-align: right\"/>","tags":[{"name":"web","slug":"web","permalink":"http://freemeng.com/tags/web/"}]},{"title":"input_file上传美化","date":"2017-06-24T09:39:38.000Z","path":"2017/06/24/input-file上传美化/","text":"最近有一天,因为头像上传的 file 隐藏问题,加了个班,今天刚好有时间,赶紧记录一下。 先上代码,就是一个 css 样式优化。 123456789101112131415/*选择文件上传input css*/.am-form-file{ position:relative; overflow:hidden;}.am-form-file input[type=file]{ position:absolute; z-index:1; width:100%; font-size:50rem; opacity:0; cursor:pointer;} html 文件123456<li class=\"am-form-file\"> <div class=\"weui-cell__ft\"> <img id=\"icon\" src=\"${member.face!''}\"class=\"user-img\"> </div> <input id=\"file1\" name=\"file\" type=\"file\" hidefocus=\"true\" onchange=\"uploadyasuo(this.files,this.id);\"accept=\"image/*\"></li> 注意重点,就是在 file 的父级容器设置样式,容器可以是 li div 等。","tags":[{"name":"web","slug":"web","permalink":"http://freemeng.com/tags/web/"}]},{"title":"git教程推荐","date":"2017-06-24T09:30:56.000Z","path":"2017/06/24/git教程推荐/","text":"网络上 git 教程很多,首推廖雪峰 git 教程。廖雪峰 git 教程地址另外我在 github 上记录了学习笔记,包括常用 git 常用命令及学习 git 过程中遇到的问题等,另外欢迎来测试 pull request。我的学习笔记最后推荐一下其他文章: 如果对 git 的一些命令不理解,请阅读下面的文章图解 git git 入门必读git - the simple guide 更进一步高效使用Git","tags":[{"name":"git","slug":"git","permalink":"http://freemeng.com/tags/git/"}]},{"title":"github博客搭建整理","date":"2017-06-11T01:45:27.000Z","path":"2017/06/11/github博客搭建整理/","text":"写这篇文章的出发点很简单,好久没有维护 github 上的博客了,完全不记得如何开始了,今天准备整理一下相关知识。 一、概念说明github page 官方介绍地址:github page Github Pages 免费的静态站点,其特点:免费托管、自带主题、支持自制页面等。 GitHub Pages 本用于介绍托管在 GitHub 的项目, 不过,由于他的空间免费稳定,用来做搭建一个博客再好不过了。 github Pages 可以被认为是用户编写的、托管在 github 上的静态网页。 GitHub Pages 分两种,一种是你的 GitHub 用户名建立的username.github.io 这样的用户&组织页(站),另一种是依附项目的 pages。想建立个人博客是用的第一种,形如 cnfeat.github.io 这样的可访问的站,每个用户名下面只能建立一个。 hexo Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。 git node.js 使用github pages服务搭建博客的好处有:全是静态文件,访问速度快;免费方便,不用花一分钱就可以搭建一个自由的个人博客,不需要服务器不需要后台;可以随意绑定自己的域名,不仔细看的话根本看不出来你的网站是基于github的;数据绝对安全,基于github的版本管理,想恢复到哪个历史版本都行;博客内容可以轻松打包、转移、发布到其它平台;等等; 原理由于github pages存放的都是静态文件,博客存放的不只是文章内容,还有文章列表、分类、标签、翻页等动态内容,假如每次写完一篇文章都要手动更新博文目录和相关链接信息,相信谁都会疯掉,所以hexo所做的就是将这些md文件都放在本地,每次写完文章后调用写好的命令来批量完成相关页面的生成,然后再将有改动的页面提交到github。","tags":[{"name":"github博客","slug":"github博客","permalink":"http://freemeng.com/tags/github博客/"}]},{"title":"github博客个人记录","date":"2017-06-11T01:44:02.000Z","path":"2017/06/11/github博客个人记录/","text":"samsung 博客目录D:\\blog\\blog hexo 配置文件如上图中的 _config.yml 目前使用的主题theme: yilia github关联 hexo s是开启本地预览服务,打开浏览器访问 http://localhost:4000 即可看到内容,很多人会碰到浏览器一直在转圈但是就是加载不出来的问题,一般情况下是因为端口占用的缘故。 主题目录 命令解释hexo clean 清理 public 内容hexo g 生成静态页hexo s 启动本地预览服务,端口为 4000hexo d 部署到 github 上传到github如果你一切都配置好了,发布上传很容易,一句 hexo d 就搞定,当然关键还是你要把所有东西配置好。 保存 cname 等文件提交之后网页上一看,发现以前其它代码都没了,此时不要慌,一些非md文件可以把他们放到source文件夹下,这里的所有文件都会原样复制(除了md文件)到public目录的:由于 hexo 默认会把所有 md 文件都转换成 html,包括 README.md,所有需要每次生成之后、上传之前,手动将README.md复制到 public 目录,并删除 README.html。 写博客 如何让博文列表不显示全部内容","tags":[{"name":"github博客","slug":"github博客","permalink":"http://freemeng.com/tags/github博客/"}]},{"title":"github写博客步骤","date":"2017-06-11T01:38:08.000Z","path":"2017/06/11/github写博客步骤/","text":"移植简书博客 cd 到 hexo 博客目录 使用 hexo 命令生成新的文档 打开新建文档,完成博客内容 在适当位置加 1<!--more--> hexo d -g 生成并提交即可,要是本地查看 hexo s -g备注:如果发生异常,可以使用命令 hexo clean 清理 public 目录","tags":[{"name":"github博客","slug":"github博客","permalink":"http://freemeng.com/tags/github博客/"}]},{"title":"我的数字资产保护","date":"2017-06-03T01:55:31.000Z","path":"2017/06/03/我的数字资产保护/","text":"随着在线支付(支付宝等)的发展,不使用现金生活已成现实,而网络理财导致很多钱都在网络账户中,如果有一天挂了,那么其他人想取出里面的钱估计会比较费劲。最近又开始了听播客,王掌柜的节目不错,也学到了一些很 cool 的知识,数字资产保护这个词,就是从他那听到的。所以打算今天就处理一下,自己的数字资产。 我的数字资产 余额宝 百度理财 桔子理财 懒人理财 同花顺炒股一看,还不少,虽然没几个钱,但是鸡蛋还是不能放到一个篮子里的,哈哈。 场景设想 恩,就是突然。。。你懂得。。。其实拿到手机不知道密码,没关系,都是可以进行找回的。那这时的关键就是手机的密码了,这个需要告知,恩,先记录一下需要告知的,告知方式我们一会儿再说。如果有手势解锁要一并告知,尤其一些软件有开锁设置。突然想到,在使用支付宝转账时,还要输入指纹和密码,那么,这个密码是要告知的,不然,账号进去了,钱也取不出来啊? 说一下密码关于密码,我们还是要考虑一下,账号都是需要密码的,我目前的策略能第三方登陆的,我就不注册,但不知道这种方式安全不? 密码都很重要么?我不认为都很重要,当然我只是一个普通人,因此那些社交账号,那些没有钱的账号,密码并不是很重要。好像跑题了,这些里面没有资产,但是我要将密码就要说。 如何设置密码也许你所有账号都是一个密码,这样密码一般就不会忘记了,但是安全性很低。王掌柜推荐了几次密码管理器,身边的极客朋友也有使用收费密码管理器的,我尝试了王掌柜推荐的密码管理器感觉不是很方便。我建议这样处理密码,将密码分为几类 第一类 基础类这类密码一定程度上依赖实体,它们通常只能设置数字,一般比较简单,例如:手机开机密码,银行卡密码等。 第二类 复杂不重要类这类密码一般都是网络账号密码,例如:论坛密码,知乎密码等。 第三类 复杂重要类这类密码一般都与钱相关,例如:支付包密码,邮箱密码等。建议第一类和第二类根据自己的密码策略,自己生成密码。第三类使用密码生成器。 master password这是王掌柜推荐的密码生成器,它是根据你输入的用户名和主密码及一个密码标识,然后通过选择的算法,生成密码。输入项一致,那么得到的密码就是一致的。它没有上传存储你的密码,因此很安全。 告知方式听说 gmail 有这个功能,但是没找到,找到后再更新。所以,你的告知方式可以是口述或者纸质记录。 总结数字资产保护,我们要针对重要的账户设置安全性高的密码,例如使用密码生成器。我们要告知亲友,你的密码策略。包括:手机开机密码,应用锁密码,一般支付密码及密码生成器主密码及密码标识。","tags":[{"name":"随笔","slug":"随笔","permalink":"http://freemeng.com/tags/随笔/"}]},{"title":"正则表达式","date":"2017-05-06T13:56:41.000Z","path":"2017/05/06/正则表达式/","text":"regex 正则表达式 首先贴出两个网址:第一个《正则表达式30分钟入门教程》http://deerchao.net/tutorials/regex/regex.htm第二个 在线正则测试网站http://tool.oschina.net/regex/ what is regex?正则表达式就是 字符串规则使用正则表达式,就是1.为了找到符合规则的字符串,例如爬虫中的应用;2.为了验证字符串是否符合规则,例如用户表单校验。 base Content ^匹配你要用来查找的字符串的开头,$匹配结尾。javascript 中使用要使用 // 对表达式进行包裹,java 中直接使用即可。javascript 使用例子:1/^[a-zA-Z0-9]{6,20}$/ project example以下内容为自己项目中使用过的正则表达式java中应用123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596/** * * @Description:手机号码校验 * @author: * @time:2016年12月14日 下午12:41:41 */public static boolean telephoneNumCheck(String telephoneNum) { Pattern p = Pattern .compile(\"^(((13[0-9]{1})|(15[0-9]{1})|(17[0-9]{1})|(18[0-9]{1}))+\\\\d{8})$\"); Matcher m = p.matcher(telephoneNum); return m.matches();}/** * * @Description:身份证号码校验 * @author: * @time:2016年12月14日 下午12:43:05 */public static boolean idCardNumCheck(String idCardNum) { Pattern p = Pattern .compile(\"^(^[1-9]\\\\d{7}((0\\\\d)|(1[0-2]))(([0|1|2]\\\\d)|3[0-1])\\\\d{3}$)|(^[1-9]\\\\d{5}[1-9]\\\\d{3}((0\\\\d)|(1[0-2]))(([0|1|2]\\\\d)|3[0-1])((\\\\d{4})|\\\\d{3}[Xx])$)$\"); Matcher m = p.matcher(idCardNum); if (m.matches()) { if (idCardNum.length() == 18) { Integer[] idCardWi = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 }; // 这是除以11后,可能产生的11位余数、验证码,也保存成数组 Integer[] idCardY = { 1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2 }; // 用来保存前17位各自乖以加权因子后的总和 Integer idCardWiSum = 0; for (int i = 0; i < 17; i++) { idCardWiSum += Integer.parseInt(idCardNum.substring(i, i + 1)) * idCardWi[i]; } int idCardMod = idCardWiSum % 11;// 计算出校验码所在数组的位置 String idCardLast = idCardNum.substring(17);// 得到最后一位身份证号码 if (idCardMod == 2) { if (\"X\".equals(idCardLast) || \"x\".equals(idCardLast)) { return true; } else { return false; } } else { // 用计算出的验证码与最后一位身份证号码匹配,如果一致,说明通过,否则是无效的身份证号码 if (Integer.parseInt(idCardLast) == idCardY[idCardMod]) { return true; } else { return false; } } }else{ return true; } } else { return false; }}/** * * @Description:邮编校验 * @author: * @time:2016年12月14日 下午2:01:21 */public static boolean zipCodeCheck(String zipCode) { Pattern p = Pattern.compile(\"^[1-9]\\\\d{5}$\"); Matcher m = p.matcher(zipCode); return m.matches();}/** * * @Description:身份证姓名校验 * @author: * @time:2016年12月14日 下午2:23:49 */public static boolean nameCheck(String name) { Pattern p = Pattern .compile(\"[\\\\u4E00-\\\\u9FA5]{2,5}(?:·[\\\\u4E00-\\\\u9FA5]{2,5})*\"); Matcher m = p.matcher(name); return m.matches();}/** * 过滤特殊字符 * @param str * @return */public static String specialCharacterFilter(String str){ String regEx = \"[`~!@#$%^&*()+=|{}':;',\\\\[\\\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]\"; Pattern p = Pattern.compile(regEx); Matcher m = p.matcher(str); return m.replaceAll(\"\").trim(); } javascript 应用1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283/**校验手机号*/$.validator.addMethod(\"mobilePhone\",function(value){ var myreg = /^(((13[0-9]{1})|(15[0-9]{1})|(17[0-9]{1})|(18[0-9]{1}))+\\d{8})$/; if(myreg.test(value)){ return true; }else { return false; }},\"请输入正确的手机号码\");/**校验长度*/$.validator.addMethod(\"checkNameLen\", function(value) { if(/^[\\u4e00-\\u9fa5a-zA-Z0-9]{2,20}$/.test(value)) { return true; } return false;}, \"请输入2-20个汉字或2-20位字符,不包含特殊字符\");/**校验长度*/$.validator.addMethod(\"checkUnameLen\", function(value) { if(/^[a-zA-Z0-9]{6,20}$/.test(value)){ return true; } return false;}, \"请输入6-20位字符,不包含汉字及特殊字符\");/**密码校验**/$.validator.addMethod(\"checkPassword\", function(value) { if(/^[a-zA-Z0-9_]{6,20}$/.test(value)){ return true; } return false;}, \"请输入6-20位数字、字母或下划线\");/** * 校验全局唯一用户名 */$.validator.addMethod(\"primaryUname\", function(value, element) { var succ = false; if($(\"#customerId\").val()!=\"\"){ succ = true; } var username=$(\"#uname\").val(); if(username == value){ succ=true }else{ $.ajax({ type : \"POST\", url : unameCkeckPath, data : {nickName : value}, async : false, success : function(msg) { if (msg == \"succ\") { succ = true } } });} return succ;}, \"账户名已存在\");$.validator.addMethod(\"pwdEqUname\", function(value) { var username = $(\"#userName\").val(); if (value != username) { return true; } else { return false; }}, \"密码不能和用户名一样\");//检查第二次输入密码是否与上面的一样$.validator.addMethod(\"repassEqual\", function(value) { var pwd1 = $(\"#loginPassword\").val(); var pwd2 = value; if (pwd1==pwd2) { return true; } else { return false; }}, \"两次输入密码不一样\");// 检查邮箱,为空不检查$.validator.addMethod(\"email\", function(value) { var isNul = false; if(!value){isNul = true} return isNul || /^\\w+((-\\w+)|(\\.\\w+))*\\@[A-Za-z0-9]+((\\.|-)[A-Za-z0-9]+)*\\.[A-Za-z0-9]+$/.test(value);}, \"请输入正确的邮箱\");","tags":[{"name":"正则","slug":"正则","permalink":"http://freemeng.com/tags/正则/"}]},{"title":"单例设计模式学习","date":"2017-03-18T09:48:23.000Z","path":"2017/03/18/单例设计模式学习/","text":"昨天巧合看到了单例设计模式,便一发不可收拾,记录一下。 首先推荐一篇博客,地址如下:http://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html#3641445这是我看到单例设计模式写的最好的,文中介绍了饿汉式、懒汉式、DCL、类级内部类,最后说到了枚举内部类。真的写的很好,非常感谢作者!看了大量的实现,最终看到了枚举内部类,这个是《effective java》中提到的,准备买了读一读,很早就听说了这本书,但是一直没买。 什么时候用单例呢?网上查了一下,都说连接池、线程池,什么资源管理器,回家后又把《大话设计模式》翻了出来,读了一下,里面是讲得图像化界面的工具箱,对于我来说,没有用了。 早上坐车,没事我就继续查,一直在用spring框架,对象都让他创建了,那个是单例么?又温习了一下,spring 创建对象的三种方式,包括构造方法创建,静态方法创建,工厂方法创建,这些都是配置xml文档。推荐阅读链接:http://www.cnblogs.com/LiuChunfu/p/5574383.html 不对啊,我一直在用注解啊?继续查。@Autowired @Resource 原来@Autowired 是spring的注解,而@Resource 是JAVAEE的注解,文章说为了避免耦合,建议使用@Resource ,但是 spring 一定会使用啊,害怕耦合么?继续翻,知道了通过注解创建的对象,默认用的是工厂方法,默认是单例的。 总结 单例,确保类只有一个实例,实现方式: 私有构造方法; 对外提供公共访问方法; 自己创建唯一对象。使用 spring 框架,通过注解创建的 controller service dao 默认都是单例的。 以上,为个人记录,如有遗漏或错误,欢迎大家指教。","tags":[{"name":"design pattern","slug":"design-pattern","permalink":"http://freemeng.com/tags/design-pattern/"}]},{"title":"springmvc poi excel 导入","date":"2017-03-01T05:50:42.000Z","path":"2017/03/01/springmvc poi excel/","text":"本文使用的工具类参考了 http://raising.iteye.com/blog/2232167 这篇文章,由于报错做了改动,在此感谢原作者。项目源码地址:https://github.com/zmdstr/demomaven pom123456789101112 <!-- poi --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.14</version> </dependency> <!-- 文件上传 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.2.2</version></dependency> jsp12345678910111213141516171819<%@ page language=\"java\" contentType=\"text/html; charset=UTF-8\" pageEncoding=\"UTF-8\"%><!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><title>file upload</title><script type=\"text/javascript\" src=\"${pageContext.request.contextPath }/js/jquery-1.11.1.js\"></script><script type=\"text/javascript\" src=\"${pageContext.request.contextPath }/js/ajaxfileupload.js\"></script><script type=\"text/javascript\" src=\"${pageContext.request.contextPath }/js/file-upload.js\"></script></head><body><h3>批量导入excel测试</h3> <form action=\"\"> <input type=\"file\" id=\"file\" name=\"file\"/> <input type=\"button\" value=\"导入数据\" onclick=\"upload()\"> </form></body></html> file-upload.js12345678910111213141516function upload(){ console.log(\"点击上传按钮\"); $.ajaxFileUpload({ url : \"upload1\", //dataType : 'json', secureuri : false, fileElementId : 'file', success : function(res, status) { //服务器成功响应处理函数 if (status) { console.log(status); }}, error : function(res, status, e) {//服务器响应失败处理函数 alert(\"导入数据异常:文件导入过程异常。\"); } }); } springmvc.xml中需要增加的配置12345678<!-- 文件上传配置 --> <bean id=\"multipartResolver\" class=\"org.springframework.web.multipart.commons.CommonsMultipartResolver\"> <!-- 文件的最大上传大小 --> <property name=\"defaultEncoding\" value=\"UTF-8\" /> <property name=\"maxUploadSize\" value=\"1058576000\" /> <property name=\"uploadTempDir\" value=\"/uploadFile/temp/\" /> </bean> controller1234567891011121314151617181920212223242526272829303132@RequestMapping(\"/upload\") public @ResponseBody String fileUpload(@RequestParam(value = \"file\") MultipartFile excelFile) { InputStream inputStream = null; String flag = \"导入成功!\"; try { inputStream = excelFile.getInputStream(); } catch (IOException e) { e.printStackTrace(); flag = \"导入失败\"; } List<Map<String, String>> excel = ExcelImportUtil.parseExcel(inputStream); for (Map<String, String> row : excel) { String id = row.get(\"id\"); String tbUniversityName = row.get(\"tb_university_name\"); String tbUniLoc = row.get(\"tb_uni_loc\"); String tag1 = row.get(\"tag1\"); String tag2 = row.get(\"tag2\"); String tag3 = row.get(\"tag3\"); String tag4 = row.get(\"tag4\"); UniversityBaseInfo baseInfo = new UniversityBaseInfo(); baseInfo.setId(Integer.valueOf(id)); baseInfo.setTag1(tag1); baseInfo.setTag2(tag2); baseInfo.setTag3(tag3); baseInfo.setTag4(tag4); baseInfo.setTbUniLoc(tbUniLoc); baseInfo.setTbUniversityName(tbUniversityName); myService.insert(baseInfo); } return flag; }} ExcelImportUtils1234567891011121314151617181920212223242526272829303132333435363738394041424344454647public class ExcelImportUtil { public static List<Map<String, String>> parseExcel(InputStream fis) { List<Map<String, String>> data = new ArrayList<Map<String, String>>(); try { HSSFWorkbook book = new HSSFWorkbook(fis); HSSFSheet sheet = book.getSheetAt(0); int firstRow = sheet.getFirstRowNum(); int lastRow = sheet.getLastRowNum(); for (int i = firstRow; i < lastRow; i++) { Map<String, String> map = new HashMap<String, String>(); HSSFRow row = sheet.getRow(i); int firstCell = row.getFirstCellNum(); int lastCell = row.getLastCellNum(); for (int j = firstCell; j < lastCell; j++) { HSSFCell cell2 = sheet.getRow(firstRow).getCell(j); String key = cell2.getStringCellValue(); HSSFCell cell = row.getCell(j); String val = \"\"; if (null != cell) { if (cell.getCellType() == HSSFCell.CELL_TYPE_NUMERIC) { cell.setCellType(HSSFCell.CELL_TYPE_STRING); } val = cell.getStringCellValue(); } // 如果是表头,不放入map if (i == firstRow) { break; } else { map.put(key, val); } // System.out.println(map); } // 剔除表头 if (i != firstRow) { // 载入一行数据 data.add(map); System.out.println(map); } } System.out.println(data); book.close(); } catch (IOException e) { e.printStackTrace(); } return data; }}","tags":[{"name":"poi","slug":"poi","permalink":"http://freemeng.com/tags/poi/"}]},{"title":"distpicker使用记录","date":"2017-02-27T09:51:28.000Z","path":"2017/02/27/distpicker使用记录/","text":"今天使用distpicker遇到了一些问题,记录一下。插件地址 使用说明 需要引入的 js 文件123<script type=\"text/javascript\" src=\"./././js/jquery/distpicker.data.js\"></script><script type=\"text/javascript\" src=\"./././js/jquery/distpicker.js\"></script><script type=\"text/javascript\" src=\"./././js/jquery/main.js\"></script> 页面dom12345<div data-toggle=\"distpicker\"> <select id=\"eprovinceName\" data-province=\"浙江省\" name=\"provinceName\"></select> <select id=\"ecityName\" data-city=\"杭州市\" name=\"cityName\"></select> <select id=\"edistrictName\" data-district=\"西湖区\" name=\"districtName\"></select> </div> js 初始化 12345678910 $(function(){ $(\"#distpicker\").distpicker('destroy'); $(\"#distpicker\").distpicker({ province: '省份名', city: '城市名', district: '区名', autoSelect: true, placeholder: false });}); 数据回显 123456$(\"#eprovinceName\").val(data.provinceName); $(\"#eprovinceName\").trigger(\"change\"); $(\"#ecityName\").val(data.cityName); $(\"#ecityName\").trigger(\"change\"); $(\"#edistrictName\").val(data.districtName); $(\"#edetailAddress\").val(data.detailAddress); 遇到的问题 我最开始导入的是distpicker.min.js和distpicker.data.min.js 结果报错无法使用,更换非压缩板OK; 本想设置初始值”—请选择—“,结果多次尝试,以失败告终。","tags":[{"name":"js","slug":"js","permalink":"http://freemeng.com/tags/js/"}]},{"title":"gitHub-hexo-Next个人博客搭建记录","date":"2017-02-15T13:49:47.000Z","path":"2017/02/15/gitHub-hexo-Next个人博客搭建记录/","text":"最近使用 gitHub+hexo+Next 搭建了个人博客,关于如何搭建就不记录了,网上有很多教程,写的也很详细,这里记录一些我遇到的问题。 使用 gitbash 发布后,无法访问原因:查看发布后项目发现 CNAME 文件不见了解决方法:在 source 文件夹下放置一份 CNAME 网站内容存在乱码原因:站点配置文件未设置语言解决方法:在站点配置文件 _config.yml 中配置语言为中文,即 language: zh-Hans 没有标题原因:文章中没有1---title: 解决方法:1---title:","tags":[{"name":"github博客","slug":"github博客","permalink":"http://freemeng.com/tags/github博客/"}]},{"title":"SVN与GIT","date":"2017-01-30T09:53:42.000Z","path":"2017/01/30/SVN与GIT/","text":"之前一直使用SVN,准备学习一下GIT,上网查询了资料,对比一下,记录下来。 零、为什么会出现版本控制工具SVN和GIT都是版本控制工具,那么为什么会出现版本控制工具呢?在软件开发过程中,我们经常不断的重构代码,某次修改后发现,效果还不如之前的代码,这时我们需要恢复到修改之前的状态。简单的说就是备份,每次进行修改前,进行备份,后悔时再恢复原来的版本,这样我们会有很多不同版本,而SVN及GIT就是一个可以帮助我们管理版本的工具。便于多人开发,使我们可以追踪代码的更新,找到Bug的提交者。 一、什么是SVNSVN是Subversion的简称,是一个开放源代码的版本控制系统。说得简单一点SVN就是用于多个人共同开发同一个项目。 二、SVN工作流程1.从服务器获取最新代码; 2.在本地进行开发; 3.完成任务,上传到服务器。 三、SVN的缺点在我的使用中感觉最大的缺点就是如果连接不上服务器,基本就无法工作,即无法提交代码,获取代码,查看历史版本等。 四、什么是GITGit是一个开源的分布式版本控制系统。 五、放两张图片对比一下","tags":[{"name":"git","slug":"git","permalink":"http://freemeng.com/tags/git/"}]},{"title":"fiddler使用记录","date":"2017-01-23T07:55:46.000Z","path":"2017/01/23/fiddler使用记录/","text":"纯粹好奇,小小实践了一下fiddler,分享一下~ 目的html5 项目使用微信浏览器访问,出现bug,准备抓取请求分析一下 工具Fiddler4 电脑设置 安装fiddler cmd 查看ip地址 启动fiddler,设置 Tool->Options->如下图:手机设置 连接WIFI(保证与fiddler安装的电脑在同一WIFI下),点击链接的WIFI进行高级设置,注意需要再次输入密码,高级设置中选择代理,手动代理,代理设置中,host 为主机的ip地址,port 为fiddler里面默认的8888,如果被占用在启动fiddler时会提示你更改。测试这时从微信公众号进入要测试的界面,点击后,从fiddler右侧inspectors选项卡,可以看到request及response相关信息。补充 filters使用使用过程中发现好多,不想查看的请求也被抓取了,那么我们就可以使用filters如下图: 自动保存responseBody为html文件点击Rules->CustomizeRules 对文件内容进行修改 123456789101112static function OnBeforeResponse(oSession: Session) { if (m_Hide304s && oSession.responseCode == 304) { oSession[\"ui-hide\"] = \"true\"; } if (oSession.oRequest[\"User-Agent\"].indexOf(\"Android\")> -1 && oSession.HTTPMethodIs(\"CONNECT\")) {oSession.oResponse.headers[\"Connection\"] = \"Keep-Alive\"; } if (oSession.fullUrl.Contains(\"shengxuece.com\")){ oSession.utilDecodeResponse();//消除保存的请求可能存在乱码的情况 //oSession.SaveResponse(\"D:\\\\Fiddler Sessions\\\\\"+oSession.id+\"_Request.htm\",false); oSession.SaveResponseBody(\"D:\\\\Fiddler Sessions\\\\\"+oSession.id+\"_Request.htm\");} } https请求抓取fiddler相关设置如下图:使用手机访问电脑IP地址:8888(默认端口)会进入fiddler Echo Service 点击下面的 FiddlerRoot certificate 下载证书,并到系统设置中进行安装,然后就可以顺利抓取https 了。","tags":[{"name":"fiddler","slug":"fiddler","permalink":"http://freemeng.com/tags/fiddler/"}]},{"title":"mybatis异常","date":"2017-01-20T09:59:00.000Z","path":"2017/01/20/mybatis异常/","text":"异常描述attempted to return null from a method with a primitive return type (int) 解决方式使用mysql的ifnull函数 异常发生原因我的项目ssm框架,在实现一个查询功能时,由于查询结果返回null,而mybatis 封装的sql应该返回int类型,导致抛出上面异常。使用mysql的ifnull函数,当结果为null时,函数返回0,这样就解决了异常。 ifnull函数用法MYSQL IFNULL(expr1,expr2)如果expr1不是NULL,IFNULL()返回expr1,否则它返回expr2。简单讲,将那个返回值为null的列放在expr1处,expr2写0,这样返回null时,就会显示0,可以参考我下面的例子:IFNULL()返回一个数字或字符串值,取决于它被使用的上下文环境。 补充mysql 函数之前使用的不多,就使用过一些聚合函数,count()、sun()、max()、min()等,今天学习了ifnull函数,顺便搜索了一下,还有好多函数,例如:数学函数、字符串函数、日期和时间函数、加密函数、控制流函数、格式化函数、类型转换函数和系统信息函数等。","tags":[{"name":"mybatis","slug":"mybatis","permalink":"http://freemeng.com/tags/mybatis/"}]},{"title":"mybatis_mapper_异常","date":"2017-01-20T01:57:32.000Z","path":"2017/01/20/mybatis-mapper-异常/","text":"异常描述Could not find parameter map java.util.Map 异常原因1<select id=\"selectNumAddToShopCollect\" parameterMap=\"java.util.Map\" resultType=\"int\"> 将 parameterType 错写成了 parameterMap 总结mapper 文件中的错误,好多都是 parameterMap 写成了parameterType 之类的,而且报错的位置提示并不具体,需要自己慢慢找。","tags":[{"name":"mybatis","slug":"mybatis","permalink":"http://freemeng.com/tags/mybatis/"}]}]