简单介绍一下 2020 年的目标,及具体完成情况.
| 目标 | 具体 | 完成情况 |
|---|---|---|
| LeetCode 题库 | 无要求 | 已解决:9/1572 |
| UmiJS 文档 | 文档、配置、API | 已完成 |
| Mock.JS | 文档 | 已完成 |
| UmiJS-Hooks | 文档 | 已完成 |
| 科幻小说 | 带上她的眼睛 | |
| immer.js | 文档 | 已完成 |
| SSR - UmiJS | 文档 | 已完成 |
| MyBatis-Plus | 文档 | 已完成 |
| Java 开发手册(泰山版) | Ali p3c 地址 | 已完成 |
| CSS - Grid 布局 | 慕课网 | 9.3 完成 |
简单介绍一下 2020 年的目标,及具体完成情况.
| 目标 | 具体 | 完成情况 |
|---|---|---|
| LeetCode 题库 | 无要求 | 已解决:9/1572 |
| UmiJS 文档 | 文档、配置、API | 已完成 |
| Mock.JS | 文档 | 已完成 |
| UmiJS-Hooks | 文档 | 已完成 |
| 科幻小说 | 带上她的眼睛 | |
| immer.js | 文档 | 已完成 |
| SSR - UmiJS | 文档 | 已完成 |
| MyBatis-Plus | 文档 | 已完成 |
| Java 开发手册(泰山版) | Ali p3c 地址 | 已完成 |
| CSS - Grid 布局 | 慕课网 | 9.3 完成 |
传统的获取一个类的实例,是通过提供类的构造器(构造方法)。
静态工厂方法:返回类的实例的静态方法,与设计模式中的“工厂模式”不同。
静态工厂方法与构造器相比的五大优势:
Immer,用于处理不可变状态,基于“写时复制”机制。
基本思想为:将所有更改都应用于临时的 draftState,它是 currentState 的代理。一旦完成所有突变,Immer 将基于到草稿
for(int i=0;i<100;i++){ str += "hello"; } 示例中,在编译时每次循环会创建一个 StringBuilder ,然后进行 append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。yyyy-MM-dd HH:mm:ssSystem.currentTimeMillis()java.sql.Date、java.sql.Time、java.sql.Timestampjava.util.Date.after(Date) 入参是 java.sql.Timestamp 时,会触发 JDK BUG,可能导致意外结果。LocalDate.now().lengthOfYear() 获取今年的天数LocaDate.of(2011, 1, 1).lengthOfYear() 获取指定某年的天数<? extends T> 来接收返回的数据,此写法的泛型集合不能使用add 方法,而 <? super T> 不能使用 get 方法,两者在接口调用赋值的场景中容易出错。获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
线程池不允许使用 Executors 创建,而是通过 ThreadPollExecutor 的方式。
SimpleDateFormat 是线程不安全的类,一般不要定义为 static,如果定义为 static 必须加锁。或使用 DateUtils 工具类。
必须回收自定义的 ThreadLocal 变量,尽量在 try-finally 快进行回收。
高并发时,同步调用应该去考量锁的性能消耗。能用无锁数据结构,就不要用锁;能用锁区块,就不要锁方法体;能用对象锁,就不要类锁。
对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。
在使用阻塞等待获取锁的方式中,必须在 tru 代码块之外,并且在加锁方法与 try 代码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在 finally 中无法解锁。
// 正例:
Lock lock = new XxxLock();
lock.lock();
try{
doSomething();
doOthers();
} finally {
lock.unlock();
}
在使用尝试机制来获取锁的方式中,进入业务代码块之前,必须先判断线程是否持有锁。锁的释放规则与锁的阻塞等待方式相同。
// 正例:
Lock lock = new XxxLock();
boolean isLocked = lock.tryLock();
if(isLocked) {
try{
doSomething();
doOthers();
} finally {
lock.unlock();
}
}
并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。
多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。
资金相关的金融敏感信息,使用悲观锁策略。
使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方法,线程执行代码注意 catch 异常,确保 countDown 方法被执行到。
避免 Random 实例被多线程使用,虽然共享该实例时线程安全的,但会因竞争统一 seed 导致性能下降。
通过双重检查锁实现延迟初始化的优化问题隐患,推荐解决方案中较为简单的一种:将目标属性声明为 volatile 类型。
volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但如果多写,同样无法解决线程安全问题。
HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升。
ThreadLocal 对象使用 static 修饰,ThreadLocal 无法解决共享对象的更新问题。
动态加载组件。
import React from 'react';
import { dynamic } from 'umi';
const delay = (timeout: number) =>
new Promise(resolve => setTimeout(resolve, timeout));
export default dynamic({
loader: async function() {
await delay(1000);
return () => <div>1s之后显示</div>;
},
});
可用于获取当前路由信息。
import { history } from 'umi';
// history 栈里的实体个数
console.log(history.length);
// 当前 history 跳转的 action,有 PUSH、REPLACE 和 POP 三种类型
console.log(history.action);
// location 对象,包含 pathname、search 和 hash
console.log(history.location.pathname);
console.log(history.location.search);
console.log(history.location.hash);
也可用于路由跳转
import { history } from 'umi';
// 跳转到指定路由
history.push('/list');
// 带参数跳转到指定路由
history.push('/list?a=b');
history.push({
pathname: '/list',
query: {
a: 'b',
},
});
// 跳转到上一个路由
history.goBack();
也可用于路由监听
import { history } from 'umi';
const unlisten = history.listen((location, action) => {
console.log(location.pathname);
});
unlisten();
import React from 'react';
import { Link } from 'umi';
export default () => {
return (
<div>
{/* 跳转指定路由 */}
<Link to="/about">About</Link>
{/* 跳转至附带参数的路由 */}
<Link to="/about?id=123">About 123</Link>
{/* 跳转至指定路由,附带query、hash、state */}
<Link
to={{
pathname: '/list',
search: '?sort=name',
hash: '#the-hash',
state: { fromDashboard: true },
}}
>
List
</Link>
{/* 跳转至指定路由,附带所有当前 location 上的参数 */}
<Link
to={location => {
return { ...location, pathname: '/profile' };
}}
>
Profile
</Link>
{/* 跳转至指定路由,但会替换当前history stack中的记录 */}
<Link to="/course" replace>
Course
</Link>
{/* innerRef 允许你获取基础组件 */}
<Link to="/course" innerRef={node => {}}>
Course
</Link>
</div>
);
};
特殊版本的 <Link />。当指定路由命中时,可以附着特定样式。(可以用作顶部导航的选中定位;或者菜单栏的选中定位等)
import { NavLink } from 'umi';
export default () => {
return (
<div>
{/* 和 Link 等价 */}
<NavLink to="/about">About</NavLink>
{/* 当前路由为 /faq 时,附着 class selected */}
<NavLink to="/faq" activeClassName="selected">
FAQs
</NavLink>
{/* 当前路由为 /faq 时,附着 style */}
<NavLink
to="/faq"
activeStyle={{
fontWeight: "bold",
color: "red",
}}
>
FAQs
</NavLink>
{/* 当前路由完全匹配为 /profile 时,附着 class */}
<NavLink exact to="/profile" activeClassName="selected">
Profile
</NavLink>
{/* 当前路由为 /profile/ 时,附着 class */}
<NavLink strict to="/profile/" activeClassName="selected">
Profile
</NavLink>
{/* 当前路由为 /profile,并且 query 包含 name 时,附着 class */}
<NavLink
to="/profile"
exact
activeClassName="selected"
isActive={(match, location) => {
if (!match) {
return false;
}
return location.search.includes("name");
}}
>
Profile
</NavLink>
</div>
);
};
用户离开页面时的提示选择。
import React from 'react';
import { Prompt } from 'umi';
export default function index() {
return (
<div>
<h1>FAQS</h1>
{/* 离开页面时提示 */}
<Prompt message="你确定要离开么?" />
{/* 用户跳转指定页面是提示 */}
<Prompt
message={location => {
return location.pathname === '/me'
? '您确定要跳转到个人中心吗'
: true;
}}
/>
{/* 根据一个状态来确定是否提示 */}
<Prompt when={true} message="您确定要半途而废吗?" />
</div>
);
}
高阶组件,可以通过 withRouter 获取到 history、location、match 对象。
import { withRouter } from "umi";
export default withRouter(({ history, location, match }) => {
return (
<div>
<ul>
<li>history: {history.action}</li>
<li>location: {location.pathname}</li>
<li>match: {`${match.isExact}`}</li>
</ul>
</div>
);
});
hooks,用于获取 history 对象
hooks,用于获取 location 对象
hooks,用于获取 params 对象。params 对象为动态路由(如 /user/:id)里的参数键值对。
hooks,用于当前路由的信息匹配。
import {
useHistory,
useLocation,
useParams,
useRouteMatch
} from "umi";
// 假设当前路由为:/user/10086?type=user&deleted=false
export default () => {
const history = useHistory()
const location = useLocation()
const params = useParams()
const match = useRouteMatch()
return (
<div>
<ul>
<li>history: {history.action}</li>
<li>location: {location.pathname}</li>
<li>params: {JSON.stringify(params)}</li>
<li>match: {JSON.stringify(match.params)}</li>
</ul>
</div>
);
};