auto commit

This commit is contained in:
CyC2018 2018-08-12 22:53:28 +08:00
parent 0d34f6ec14
commit 9a4f6ea86c
11 changed files with 1719 additions and 1722 deletions

View File

@ -45,7 +45,7 @@
* [二进制分帧层](#二进制分帧层)
* [服务端推送](#服务端推送)
* [首部压缩](#首部压缩)
* [八、GET 和 POST 的区别](#八get-和-post-的区别)
* [八、GET 和 POST 比较](#八get-和-post-比较)
* [作用](#作用)
* [参数](#参数)
* [安全](#安全)
@ -61,13 +61,13 @@
## URL
- URIUniform Resource Identifier统一资源标识符
- URLUniform Resource Locator统一资源定位符
- URNUniform Resource Name统一资源名称例如 urn:isbn:0-486-27557-4。
URI 包含 URL 和 URN目前 WEB 只有 URL 比较流行,所以见到的基本都是 URL。
<div align="center"> <img src="../pics//f716427a-94f2-4875-9c86-98793cf5dcc3.jpg" width="400"/> </div><br>
- URIUniform Resource Identifier统一资源标识符
- URLUniform Resource Locator统一资源定位符
- URNUniform Resource Name统一资源名称
<div align="center"> <img src="../pics//urlnuri.jpg" width="600"/> </div><br>
## 请求和响应报文
@ -197,7 +197,7 @@ CONNECT www.example.com:443 HTTP/1.1
- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
- **206 Partial Content** :表示客户端进行了范围请求响应报文包含由 Content-Range 指定范围的实体内容。
- **206 Partial Content** :表示客户端进行了范围请求响应报文包含由 Content-Range 指定范围的实体内容。
## 3XX 重定向
@ -219,7 +219,7 @@ CONNECT www.example.com:443 HTTP/1.1
- **401 Unauthorized** 该状态码表示发送的请求需要有认证信息BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。
- **403 Forbidden** :请求被拒绝,服务器端没有必要给出拒绝的详细理由
- **403 Forbidden** :请求被拒绝。
- **404 Not Found**
@ -331,7 +331,7 @@ Set-Cookie: tasty_cookie=strawberry
[page content]
```
客户端之后对同一个服务器发送请求时,会从浏览器中读出 Cookie 信息通过 Cookie 请求首部字段发送给服务器。
客户端之后对同一个服务器发送请求时,会从浏览器中取出 Cookie 信息并通过 Cookie 请求首部字段发送给服务器。
```html
GET /sample_page.html HTTP/1.1
@ -382,9 +382,9 @@ Path 标识指定了主机下的哪些路径可以接受 Cookie该 URL 路径
除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。
Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在内存型数据库中,比如 Redis,效率会更高。
Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。
使用 Session 维护用户登录的过程如下:
使用 Session 维护用户登录状态的过程如下:
- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中;
- 服务器验证该用户名和密码;
@ -499,7 +499,7 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
### 1. 短连接与长连接
当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要断开一次 TCP 连接,连接建立和断开的开销会很大。
当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。
长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。
@ -688,7 +688,7 @@ HTTPs 并不是新协议,而是让 HTTP 先和 SSLSecure Sockets Layer
### 3. HTTPs 采用的加密方式
HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证安全性,之后使用对称密钥加密进行通信来保证效率。(下图中的 Session Key 就是对称密钥)
HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率。(下图中的 Session Key 就是对称密钥)
<div align="center"> <img src="../pics//How-HTTPS-Works.png" width="600"/> </div><br>
@ -717,7 +717,7 @@ HTTPs 的报文摘要功能之所以安全,是因为它结合了加密和认
## HTTPs 的缺点
- 因为需要进行加密解密等过程,因此速度会更慢;
- 需要支付证书授权的高费用。
- 需要支付证书授权的高费用。
## 配置 HTTPs
@ -727,7 +727,7 @@ HTTPs 的报文摘要功能之所以安全,是因为它结合了加密和认
## HTTP/1.x 缺陷
HTTP/1.x 实现简单是以牺牲应用性能为代价的:
HTTP/1.x 实现简单是以牺牲性能为代价的:
- 客户端需要使用多个连接才能实现并发和缩短延迟;
- 不会压缩请求和响应首部,从而导致不必要的网络流量;
@ -763,7 +763,7 @@ HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见
<div align="center"> <img src="../pics//_u4E0B_u8F7D.png" width="600"/> </div><br>
# 八、GET 和 POST 的区别
# 八、GET 和 POST 比较
## 作用
@ -870,6 +870,7 @@ DELETE /idX/delete HTTP/1.1 -> Returns 404
- [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP)
- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn)
- [htmlspecialchars](http://php.net/manual/zh/function.htmlspecialchars.php)
- [Difference between file URI and URL in java](http://java2db.com/java-io/how-to-get-and-the-difference-between-file-uri-and-url-in-java)
- [How to Fix SQL Injection Using Java PreparedStatement & CallableStatement](https://software-security.sans.org/developer-how-to/fix-sql-injection-in-java-using-prepared-callable-statement)
- [浅谈 HTTP 中 Get 与 Post 的区别](https://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html)
- [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/)

View File

@ -25,7 +25,7 @@
## Collection
<div align="center"> <img src="../pics//NP4z3i8m38Ntd28NQ4_0KCJ2q044Oez.png"/> </div><br>
<div align="center"> <img src="../pics//VP4n3i8m34Ntd28NQ4_0KCJ2q044Oez.png"/> </div><br>
### 1. Set
@ -129,12 +129,67 @@ private static final int DEFAULT_CAPACITY = 10;
ArrayList 基于数组实现,并且具有动态扩容特性,因此保存元素的数组不一定都会被使用,那么就没必要全部进行序列化。
保存元素的数组 elementData 使用 transient 修饰,该关键字声明数组默认不会被序列化。ArrayList 重写了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部分内容。
保存元素的数组 elementData 使用 transient 修饰,该关键字声明数组默认不会被序列化。
```java
transient Object[] elementData; // non-private to simplify nested class access
```
ArrayList 实现了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部分内容。
```java
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
```
```java
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
```
序列化时需要使用 ObjectOutputStream 的 writeObject() 将对象转换为字节流并输出。而 writeObject() 方法在传入的对象存在 writeObject() 的时候会去反射调用该对象的 writeObject() 来实现序列化。反序列化使用的是 ObjectInputStream 的 readObject() 方法,原理类似。
```java
ArrayList list = new ArrayList();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(list);
```
### 3. 扩容
添加元素时使用 ensureCapacityInternal() 方法来保证容量足够,如果不够时,需要使用 grow() 方法进行扩容,新容量的大小为 `oldCapacity + (oldCapacity >> 1)`,也就是旧容量的 1.5 倍。
@ -1080,7 +1135,7 @@ Set <|.. LinkedHashSet
SortSet <|.. TreeSet
List <|.. ArrayList
List <|.. Vector
List <|.. LinkeList
List <|.. LinkedList
Queue <|.. LinkedList
Queue <|.. PriorityQueue

File diff suppressed because it is too large Load Diff

View File

@ -461,7 +461,7 @@ Employee 表:
+----+-------+--------+-----------+
```
查找所有员工,他们的薪资大于其经理薪资。
查找薪资大于其经理薪资的员工信息
## SQL Schema
@ -924,27 +924,27 @@ VALUES
```sql
SELECT
s1.id - 1 AS id,
s1.student
s1.student
FROM
seat s1
seat s1
WHERE
s1.id MOD 2 = 0 UNION
SELECT
s2.id + 1 AS id,
s2.student
s2.student
FROM
seat s2
seat s2
WHERE
s2.id MOD 2 = 1
s2.id MOD 2 = 1
AND s2.id != ( SELECT max( s3.id ) FROM seat s3 ) UNION
SELECT
s4.id AS id,
s4.student
s4.student
FROM
seat s4
seat s4
WHERE
s4.id MOD 2 = 1
AND s4.id = ( SELECT max( s5.id ) FROM seat s5 )
s4.id MOD 2 = 1
AND s4.id = ( SELECT max( s5.id ) FROM seat s5 )
ORDER BY
id;
```

View File

@ -633,11 +633,12 @@ public int RectCover(int n) {
## 解题思路
当 nums[m] <= nums[h] 的情况下,说明解在 [l, m] 之间,此时令 h = m否则解在 [m + 1, h] 之间,令 l = m + 1。
- 当 nums[m] <= nums[h] 的情况下,说明解在 [l, m] 之间,此时令 h = m
- 否则解在 [m + 1, h] 之间,令 l = m + 1。
因为 h 的赋值表达式为 h = m因此循环体的循环条件应该为 l < h详细解释请见 [Leetcode 题解](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3.md#%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE) 二分查找部分
因为 h 的赋值表达式为 h = m因此循环体的循环条件应该为 l < h详细解释请见 [Leetcode 题解](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3.md) 二分查找部分
但是如果出现 nums[l] == nums[m] == nums[h],那么此时无法确定解在哪个区间,因此需要切换到顺序查找。
但是如果出现 nums[l] == nums[m] == nums[h],那么此时无法确定解在哪个区间,需要切换到顺序查找。
复杂度O(logN) + O(1)
@ -1217,8 +1218,7 @@ public ListNode ReverseList(ListNode head) {
### 递归
```java
public ListNode Merge(ListNode list1, ListNode list2)
{
public ListNode Merge(ListNode list1, ListNode list2) {
if (list1 == null)
return list2;
if (list2 == null)
@ -1236,8 +1236,7 @@ public ListNode Merge(ListNode list1, ListNode list2)
### 迭代
```java
public ListNode Merge(ListNode list1, ListNode list2)
{
public ListNode Merge(ListNode list1, ListNode list2) {
ListNode head = new ListNode(-1);
ListNode cur = head;
while (list1 != null && list2 != null) {
@ -1269,15 +1268,13 @@ public ListNode Merge(ListNode list1, ListNode list2)
## 解题思路
```java
public boolean HasSubtree(TreeNode root1, TreeNode root2)
{
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
if (root1 == null || root2 == null)
return false;
return isSubtreeWithRoot(root1, root2) || HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
}
private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2)
{
private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2) {
if (root2 == null)
return true;
if (root1 == null)
@ -1298,9 +1295,10 @@ private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2)
## 解题思路
### 递归
```java
public void Mirror(TreeNode root)
{
public void Mirror(TreeNode root) {
if (root == null)
return;
swap(root);
@ -1308,14 +1306,36 @@ public void Mirror(TreeNode root)
Mirror(root.right);
}
private void swap(TreeNode root)
{
private void swap(TreeNode root) {
TreeNode t = root.left;
root.left = root.right;
root.right = t;
}
```
### 迭代
```java
public void Mirror(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
if (node == null)
continue;
swap(node);
stack.push(node.left);
stack.push(node.right);
}
}
private void swap(TreeNode node) {
TreeNode t = node.left;
node.left = node.right;
node.right = t;
}
```
# 28 对称的二叉树
[NowCder](https://www.nowcoder.com/practice/ff05d44dfdb04e1d83bdbdab320efbcb?tpId=13&tqId=11211&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@ -1327,15 +1347,13 @@ private void swap(TreeNode root)
## 解题思路
```java
boolean isSymmetrical(TreeNode pRoot)
{
boolean isSymmetrical(TreeNode pRoot) {
if (pRoot == null)
return true;
return isSymmetrical(pRoot.left, pRoot.right);
}
boolean isSymmetrical(TreeNode t1, TreeNode t2)
{
boolean isSymmetrical(TreeNode t1, TreeNode t2) {
if (t1 == null && t2 == null)
return true;
if (t1 == null || t2 == null)
@ -1359,8 +1377,7 @@ boolean isSymmetrical(TreeNode t1, TreeNode t2)
## 解题思路
```java
public ArrayList<Integer> printMatrix(int[][] matrix)
{
public ArrayList<Integer> printMatrix(int[][] matrix) {
ArrayList<Integer> ret = new ArrayList<>();
int r1 = 0, r2 = matrix.length - 1, c1 = 0, c2 = matrix[0].length - 1;
while (r1 <= r2 && c1 <= c2) {
@ -1394,25 +1411,21 @@ public ArrayList<Integer> printMatrix(int[][] matrix)
private Stack<Integer> dataStack = new Stack<>();
private Stack<Integer> minStack = new Stack<>();
public void push(int node)
{
public void push(int node) {
dataStack.push(node);
minStack.push(minStack.isEmpty() ? node : Math.min(minStack.peek(), node));
}
public void pop()
{
public void pop() {
dataStack.pop();
minStack.pop();
}
public int top()
{
public int top() {
return dataStack.peek();
}
public int min()
{
public int min() {
return minStack.peek();
}
```
@ -1430,8 +1443,7 @@ public int min()
使用一个栈来模拟压入弹出操作。
```java
public boolean IsPopOrder(int[] pushSequence, int[] popSequence)
{
public boolean IsPopOrder(int[] pushSequence, int[] popSequence) {
int n = pushSequence.length;
Stack<Integer> stack = new Stack<>();
for (int pushIndex = 0, popIndex = 0; pushIndex < n; pushIndex++) {
@ -1464,8 +1476,7 @@ public boolean IsPopOrder(int[] pushSequence, int[] popSequence)
不需要使用两个队列分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。
```java
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root)
{
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
ArrayList<Integer> ret = new ArrayList<>();
queue.add(root);
@ -1495,8 +1506,7 @@ public ArrayList<Integer> PrintFromTopToBottom(TreeNode root)
## 解题思路
```java
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot)
{
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(pRoot);
@ -1529,8 +1539,7 @@ ArrayList<ArrayList<Integer>> Print(TreeNode pRoot)
## 解题思路
```java
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot)
{
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(pRoot);
@ -1571,15 +1580,13 @@ public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot)
## 解题思路
```java
public boolean VerifySquenceOfBST(int[] sequence)
{
public boolean VerifySquenceOfBST(int[] sequence) {
if (sequence == null || sequence.length == 0)
return false;
return verify(sequence, 0, sequence.length - 1);
}
private boolean verify(int[] sequence, int first, int last)
{
private boolean verify(int[] sequence, int first, int last) {
if (last - first <= 1)
return true;
int rootVal = sequence[last];
@ -1610,14 +1617,12 @@ private boolean verify(int[] sequence, int first, int last)
```java
private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target)
{
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
backtracking(root, target, new ArrayList<>());
return ret;
}
private void backtracking(TreeNode node, int target, ArrayList<Integer> path)
{
private void backtracking(TreeNode node, int target, ArrayList<Integer> path) {
if (node == null)
return;
path.add(node.val);
@ -1641,8 +1646,7 @@ private void backtracking(TreeNode node, int target, ArrayList<Integer> path)
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。
```java
public class RandomListNode
{
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
@ -1670,8 +1674,7 @@ public class RandomListNode
<div align="center"> <img src="../pics//8f3b9519-d705-48fe-87ad-2e4052fc81d2.png" width="600"/> </div><br>
```java
public RandomListNode Clone(RandomListNode pHead)
{
public RandomListNode Clone(RandomListNode pHead) {
if (pHead == null)
return null;
// 插入新节点
@ -1718,14 +1721,12 @@ public RandomListNode Clone(RandomListNode pHead)
private TreeNode pre = null;
private TreeNode head = null;
public TreeNode Convert(TreeNode root)
{
public TreeNode Convert(TreeNode root) {
inOrder(root);
return head;
}
private void inOrder(TreeNode node)
{
private void inOrder(TreeNode node) {
if (node == null)
return;
inOrder(node.left);
@ -1752,21 +1753,18 @@ private void inOrder(TreeNode node)
```java
private String deserializeStr;
public String Serialize(TreeNode root)
{
public String Serialize(TreeNode root) {
if (root == null)
return "#";
return root.val + " " + Serialize(root.left) + " " + Serialize(root.right);
}
public TreeNode Deserialize(String str)
{
public TreeNode Deserialize(String str) {
deserializeStr = str;
return Deserialize();
}
private TreeNode Deserialize()
{
private TreeNode Deserialize() {
if (deserializeStr.length() == 0)
return null;
int index = deserializeStr.indexOf(" ");
@ -1795,8 +1793,7 @@ private TreeNode Deserialize()
```java
private ArrayList<String> ret = new ArrayList<>();
public ArrayList<String> Permutation(String str)
{
public ArrayList<String> Permutation(String str) {
if (str.length() == 0)
return ret;
char[] chars = str.toCharArray();
@ -1805,8 +1802,7 @@ public ArrayList<String> Permutation(String str)
return ret;
}
private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s)
{
private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s) {
if (s.length() == chars.length) {
ret.add(s.toString());
return;
@ -1836,8 +1832,7 @@ private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s)
使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素不相等时,令 cnt--。如果前面查找了 i 个元素,且 cnt == 0说明前 i 个元素没有 majority或者有 majority但是出现的次数少于 i / 2 ,因为如果多于 i / 2 的话 cnt 就一定不会为 0 。此时剩下的 n - i 个元素中majority 的数目依然多于 (n - i) / 2因此继续查找就能找出 majority。
```java
public int MoreThanHalfNum_Solution(int[] nums)
{
public int MoreThanHalfNum_Solution(int[] nums) {
int majority = nums[0];
for (int i = 1, cnt = 1; i < nums.length; i++) {
cnt = nums[i] == majority ? cnt + 1 : cnt - 1;
@ -1868,8 +1863,7 @@ public int MoreThanHalfNum_Solution(int[] nums)
快速排序的 partition() 方法,会返回一个整数 j 使得 a[l..j-1] 小于等于 a[j],且 a[j+1..h] 大于等于 a[j],此时 a[j] 就是数组的第 j 大元素。可以利用这个特性找出数组的第 K 个元素,这种找第 K 个元素的算法称为快速选择算法。
```java
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k)
{
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
ArrayList<Integer> ret = new ArrayList<>();
if (k > nums.length || k <= 0)
return ret;
@ -1880,8 +1874,7 @@ public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k)
return ret;
}
public void findKthSmallest(int[] nums, int k)
{
public void findKthSmallest(int[] nums, int k) {
int l = 0, h = nums.length - 1;
while (l < h) {
int j = partition(nums, l, h);
@ -1894,8 +1887,7 @@ public void findKthSmallest(int[] nums, int k)
}
}
private int partition(int[] nums, int l, int h)
{
private int partition(int[] nums, int l, int h) {
int p = nums[l]; /* 切分元素 */
int i = l, j = h + 1;
while (true) {
@ -1909,8 +1901,7 @@ private int partition(int[] nums, int l, int h)
return j;
}
private void swap(int[] nums, int i, int j)
{
private void swap(int[] nums, int i, int j) {
int t = nums[i];
nums[i] = nums[j];
nums[j] = t;
@ -1927,8 +1918,7 @@ private void swap(int[] nums, int i, int j)
维护一个大小为 K 的最小堆过程如下:在添加一个元素之后,如果大顶堆的大小大于 K那么需要将大顶堆的堆顶元素去除。
```java
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k)
{
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
if (k > nums.length || k <= 0)
return new ArrayList<>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
@ -1959,8 +1949,7 @@ private PriorityQueue<Integer> right = new PriorityQueue<>();
/* 当前数据流读入的元素个数 */
private int N = 0;
public void Insert(Integer val)
{
public void Insert(Integer val) {
/* 插入要保证两个堆存于平衡状态 */
if (N % 2 == 0) {
/* N 为偶数的情况下插入到右半边。
@ -1975,8 +1964,7 @@ public void Insert(Integer val)
N++;
}
public Double GetMedian()
{
public Double GetMedian() {
if (N % 2 == 0)
return (left.peek() + right.peek()) / 2.0;
else
@ -1998,16 +1986,14 @@ public Double GetMedian()
private int[] cnts = new int[256];
private Queue<Character> queue = new LinkedList<>();
public void Insert(char ch)
{
public void Insert(char ch) {
cnts[ch]++;
queue.add(ch);
while (!queue.isEmpty() && cnts[queue.peek()] > 1)
queue.poll();
}
public char FirstAppearingOnce()
{
public char FirstAppearingOnce() {
return queue.isEmpty() ? '#' : queue.peek();
}
```
@ -2023,8 +2009,7 @@ public char FirstAppearingOnce()
## 解题思路
```java
public int FindGreatestSumOfSubArray(int[] nums)
{
public int FindGreatestSumOfSubArray(int[] nums) {
if (nums == null || nums.length == 0)
return 0;
int greatestSum = Integer.MIN_VALUE;
@ -2044,8 +2029,7 @@ public int FindGreatestSumOfSubArray(int[] nums)
## 解题思路
```java
public int NumberOf1Between1AndN_Solution(int n)
{
public int NumberOf1Between1AndN_Solution(int n) {
int cnt = 0;
for (int m = 1; m <= n; m *= 10) {
int a = n / m, b = n % m;
@ -2066,8 +2050,7 @@ public int NumberOf1Between1AndN_Solution(int n)
## 解题思路
```java
public int getDigitAtIndex(int index)
{
public int getDigitAtIndex(int index) {
if (index < 0)
return -1;
int place = 1; // 1 表示个位2 表示 十位...
@ -2085,8 +2068,7 @@ public int getDigitAtIndex(int index)
* place 位数的数字组成的字符串长度
* 10, 90, 900, ...
*/
private int getAmountOfPlace(int place)
{
private int getAmountOfPlace(int place) {
if (place == 1)
return 10;
return (int) Math.pow(10, place - 1) * 9;
@ -2096,8 +2078,7 @@ private int getAmountOfPlace(int place)
* place 位数的起始数字
* 0, 10, 100, ...
*/
private int getBeginNumberOfPlace(int place)
{
private int getBeginNumberOfPlace(int place) {
if (place == 1)
return 0;
return (int) Math.pow(10, place - 1);
@ -2106,8 +2087,7 @@ private int getBeginNumberOfPlace(int place)
/**
* 在 place 位数组成的字符串中,第 index 个数
*/
private int getDigitAtIndex(int index, int place)
{
private int getDigitAtIndex(int index, int place) {
int beginNumber = getBeginNumberOfPlace(place);
int shiftNumber = index / place;
String number = (beginNumber + shiftNumber) + "";
@ -2129,8 +2109,7 @@ private int getDigitAtIndex(int index, int place)
可以看成是一个排序问题,在比较两个字符串 S1 和 S2 的大小时,应该比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 < S2+S1那么应该把 S1 排在前面否则应该把 S2 排在前面
```java
public String PrintMinNumber(int[] numbers)
{
public String PrintMinNumber(int[] numbers) {
if (numbers == null || numbers.length == 0)
return "";
int n = numbers.length;
@ -2156,8 +2135,7 @@ public String PrintMinNumber(int[] numbers)
## 解题思路
```java
public int numDecodings(String s)
{
public int numDecodings(String s) {
if (s == null || s.length() == 0)
return 0;
int n = s.length();
@ -2200,8 +2178,7 @@ public int numDecodings(String s)
应该用动态规划求解,而不是深度优先搜索,深度优先搜索过于复杂,不是最优解。
```java
public int getMost(int[][] values)
{
public int getMost(int[][] values) {
if (values == null || values.length == 0 || values[0].length == 0)
return 0;
int n = values[0].length;
@ -2224,8 +2201,7 @@ public int getMost(int[][] values)
## 解题思路
```java
public int longestSubStringWithoutDuplication(String str)
{
public int longestSubStringWithoutDuplication(String str) {
int curLen = 0;
int maxLen = 0;
int[] preIndexs = new int[26];
@ -2257,8 +2233,7 @@ public int longestSubStringWithoutDuplication(String str)
## 解题思路
```java
public int GetUglyNumber_Solution(int N)
{
public int GetUglyNumber_Solution(int N) {
if (N <= 6)
return N;
int i2 = 0, i3 = 0, i5 = 0;
@ -2291,8 +2266,7 @@ public int GetUglyNumber_Solution(int N)
最直观的解法是使用 HashMap 对出现次数进行统计,但是考虑到要统计的字符范围有限,因此可以使用整型数组代替 HashMap。
```java
public int FirstNotRepeatingChar(String str)
{
public int FirstNotRepeatingChar(String str) {
int[] cnts = new int[256];
for (int i = 0; i < str.length(); i++)
cnts[str.charAt(i)]++;
@ -2306,8 +2280,7 @@ public int FirstNotRepeatingChar(String str)
以上实现的空间复杂度还不是最优的。考虑到只需要找到只出现一次的字符,那么我们只需要统计的次数信息只有 0,1,更大,使用两个比特位就能存储这些信息。
```java
public int FirstNotRepeatingChar2(String str)
{
public int FirstNotRepeatingChar2(String str) {
BitSet bs1 = new BitSet(256);
BitSet bs2 = new BitSet(256);
for (char c : str.toCharArray()) {
@ -2339,15 +2312,13 @@ public int FirstNotRepeatingChar2(String str)
private long cnt = 0;
private int[] tmp; // 在这里创建辅助数组,而不是在 merge() 递归函数中创建
public int InversePairs(int[] nums)
{
public int InversePairs(int[] nums) {
tmp = new int[nums.length];
mergeSort(nums, 0, nums.length - 1);
return (int) (cnt % 1000000007);
}
private void mergeSort(int[] nums, int l, int h)
{
private void mergeSort(int[] nums, int l, int h) {
if (h - l < 1)
return;
int m = l + (h - l) / 2;
@ -2356,8 +2327,7 @@ private void mergeSort(int[] nums, int l, int h)
merge(nums, l, m, h);
}
private void merge(int[] nums, int l, int m, int h)
{
private void merge(int[] nums, int l, int m, int h) {
int i = l, j = m + 1, k = l;
while (i <= m || j <= h) {
if (i > m)
@ -2392,8 +2362,7 @@ private void merge(int[] nums, int l, int m, int h)
当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B同样地当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。
```java
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2)
{
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode l1 = pHead1, l2 = pHead2;
while (l1 != l2) {
l1 = (l1 == null) ? pHead2 : l1.next;
@ -2420,15 +2389,13 @@ Output:
## 解题思路
```java
public int GetNumberOfK(int[] nums, int K)
{
public int GetNumberOfK(int[] nums, int K) {
int first = binarySearch(nums, K);
int last = binarySearch(nums, K + 1);
return (first == nums.length || nums[first] != K) ? 0 : last - first;
}
private int binarySearch(int[] nums, int K)
{
private int binarySearch(int[] nums, int K) {
int l = 0, h = nums.length;
while (l < h) {
int m = l + (h - l) / 2;
@ -2453,14 +2420,12 @@ private int binarySearch(int[] nums, int K)
private TreeNode ret;
private int cnt = 0;
public TreeNode KthNode(TreeNode pRoot, int k)
{
public TreeNode KthNode(TreeNode pRoot, int k) {
inOrder(pRoot, k);
return ret;
}
private void inOrder(TreeNode root, int k)
{
private void inOrder(TreeNode root, int k) {
if (root == null || cnt >= k)
return;
inOrder(root.left, k);
@ -2484,8 +2449,7 @@ private void inOrder(TreeNode root, int k)
## 解题思路
```java
public int TreeDepth(TreeNode root)
{
public int TreeDepth(TreeNode root) {
return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right));
}
```
@ -2505,14 +2469,12 @@ public int TreeDepth(TreeNode root)
```java
private boolean isBalanced = true;
public boolean IsBalanced_Solution(TreeNode root)
{
public boolean IsBalanced_Solution(TreeNode root) {
height(root);
return isBalanced;
}
private int height(TreeNode root)
{
private int height(TreeNode root) {
if (root == null || !isBalanced)
return 0;
int left = height(root.left);
@ -2538,8 +2500,7 @@ private int height(TreeNode root)
diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。
```java
public void FindNumsAppearOnce(int[] nums, int num1[], int num2[])
{
public void FindNumsAppearOnce(int[] nums, int num1[], int num2[]) {
int diff = 0;
for (int num : nums)
diff ^= num;
@ -2570,8 +2531,7 @@ public void FindNumsAppearOnce(int[] nums, int num1[], int num2[])
- 如果 sum < target移动较小的元素使 sum 变大一些
```java
public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum)
{
public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum) {
int i = 0, j = array.length - 1;
while (i < j) {
int cur = array[i] + array[j];
@ -2604,8 +2564,7 @@ public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum)
## 解题思路
```java
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum)
{
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
int start = 1, end = 2;
int curSum = 3;
@ -2648,8 +2607,7 @@ public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum)
正确的解法应该是和书上一样,先旋转每个单词,再旋转整个字符串。
```java
public String ReverseSentence(String str)
{
public String ReverseSentence(String str) {
int n = str.length();
char[] chars = str.toCharArray();
int i = 0, j = 0;
@ -2664,14 +2622,12 @@ public String ReverseSentence(String str)
return new String(chars);
}
private void reverse(char[] c, int i, int j)
{
private void reverse(char[] c, int i, int j) {
while (i < j)
swap(c, i++, j--);
}
private void swap(char[] c, int i, int j)
{
private void swap(char[] c, int i, int j) {
char t = c[i];
c[i] = c[j];
c[j] = t;
@ -2691,8 +2647,7 @@ private void swap(char[] c, int i, int j)
先将 "abc" 和 "XYZdef" 分别翻转,得到 "cbafedZYX",然后再把整个字符串翻转得到 "XYZdefabc"。
```java
public String LeftRotateString(String str, int n)
{
public String LeftRotateString(String str, int n) {
if (n >= str.length())
return str;
char[] chars = str.toCharArray();
@ -2702,14 +2657,12 @@ public String LeftRotateString(String str, int n)
return new String(chars);
}
private void reverse(char[] chars, int i, int j)
{
private void reverse(char[] chars, int i, int j) {
while (i < j)
swap(chars, i++, j--);
}
private void swap(char[] chars, int i, int j)
{
private void swap(char[] chars, int i, int j) {
char t = chars[i];
chars[i] = chars[j];
chars[j] = t;
@ -2727,8 +2680,7 @@ private void swap(char[] chars, int i, int j)
## 解题思路
```java
public ArrayList<Integer> maxInWindows(int[] num, int size)
{
public ArrayList<Integer> maxInWindows(int[] num, int size) {
ArrayList<Integer> ret = new ArrayList<>();
if (size > num.length || size < 1)
return ret;
@ -2762,8 +2714,7 @@ public ArrayList<Integer> maxInWindows(int[] num, int size)
空间复杂度O(N<sup>2</sup>)
```java
public List<Map.Entry<Integer, Double>> dicesSum(int n)
{
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
final int face = 6;
final int pointNum = face * n;
long[][] dp = new long[n + 1][pointNum + 1];
@ -2790,8 +2741,7 @@ public List<Map.Entry<Integer, Double>> dicesSum(int n)
空间复杂度O(N)
```java
public List<Map.Entry<Integer, Double>> dicesSum(int n)
{
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
final int face = 6;
final int pointNum = face * n;
long[][] dp = new long[2][pointNum + 1];
@ -2829,8 +2779,7 @@ public List<Map.Entry<Integer, Double>> dicesSum(int n)
## 解题思路
```java
public boolean isContinuous(int[] nums)
{
public boolean isContinuous(int[] nums) {
if (nums.length < 5)
return false;
Arrays.sort(nums);
@ -2861,8 +2810,7 @@ public boolean isContinuous(int[] nums)
约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。因为是圆圈所以最后需要对 n 取余。
```java
public int LastRemaining_Solution(int n, int m)
{
public int LastRemaining_Solution(int n, int m) {
if (n == 0) /* 特殊输入的处理 */
return -1;
if (n == 1) /* 返回条件 */
@ -2884,8 +2832,7 @@ public int LastRemaining_Solution(int n, int m)
使用贪心策略,假设第 i 轮进行卖出操作,买入操作价格应该在 i 之前并且价格最低。
```java
public int maxProfit(int[] prices)
{
public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0)
return 0;
int soFarMin = prices[0];
@ -2915,8 +2862,7 @@ public int maxProfit(int[] prices)
以下实现中,递归的返回条件为 n <= 0取非后就是 n > 0递归的主体部分为 sum += Sum_Solution(n - 1),转换为条件语句后就是 (sum += Sum_Solution(n - 1)) > 0。
```java
public int Sum_Solution(int n)
{
public int Sum_Solution(int n) {
int sum = n;
boolean b = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0);
return sum;
@ -2938,8 +2884,7 @@ a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进
递归会终止的原因是 (a & b) << 1 最右边会多一个 0那么继续递归进位最右边的 0 会慢慢增多最后进位会变为 0递归终止
```java
public int Add(int a, int b)
{
public int Add(int a, int b) {
return b == 0 ? a : Add(a ^ b, (a & b) << 1);
}
```
@ -2955,8 +2900,7 @@ public int Add(int a, int b)
## 解题思路
```java
public int[] multiply(int[] A)
{
public int[] multiply(int[] A) {
int n = A.length;
int[] B = new int[n];
for (int i = 0, product = 1; i < n; product *= A[i], i++) /* 从左往右累乘 */
@ -2988,8 +2932,7 @@ Output:
## 解题思路
```java
public int StrToInt(String str)
{
public int StrToInt(String str) {
if (str == null || str.length() == 0)
return 0;
boolean isNegative = str.charAt(0) == '-';

View File

@ -29,7 +29,7 @@
## 危害
- 窃取用户的 Cookie
- 窃取用户的 Cookie
- 伪造虚假的输入表单骗取个人信息
- 显示伪造的文章或者图片
@ -47,7 +47,7 @@
富文本编辑器允许用户输入 HTML 代码,就不能简单地将 `<` 等字符进行过滤了,极大地提高了 XSS 攻击的可能性。
富文本编辑器通常采用 XSS filter 来防范 XSS 攻击,可以定义一些标签白名单或者黑名单,从而不允许有攻击性的 HTML 代码的输入。
富文本编辑器通常采用 XSS filter 来防范 XSS 攻击,通过定义一些标签白名单或者黑名单,从而不允许有攻击性的 HTML 代码的输入。
以下例子中form 和 script 等标签都被转义,而 h 和 p 等标签将会保留。
@ -131,7 +131,7 @@ http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">
```
如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 资金
如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 美元
这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务器端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险。
@ -153,8 +153,6 @@ Referer 首部字段位于 HTTP 报文中,用于标识请求来源的地址。
因为 CSRF 攻击是在用户无意识的情况下发生的,所以要求用户输入验证码可以让用户知道自己正在做的操作。
也可以要求用户输入验证码来进行校验。
# 三、SQL 注入攻击
## 概念

View File

@ -383,13 +383,13 @@ MVCC 不能解决幻读的问题Next-Key Locks 就是为了解决这个问题
## Record Locks
锁定的对象是记录的索引,而不是记录本身。
锁定一个记录上的索引,而不是记录本身。
如果表没有设置索引InnoDB 会自动在主键上创建隐藏的聚集索引,因此 Record Locks 依然可以使用。
## Gap Locks
锁定一个范围内的索引,例如当一个事务执行以下语句,其它事务就不能在 t.c 中插入 15。
锁定索引之间的间隙,但是不包含索引本身。例如当一个事务执行以下语句,其它事务就不能在 t.c 中插入 15。
```sql
SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
@ -397,28 +397,14 @@ SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
## Next-Key Locks
它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录,也锁定范围内的索引。在 user 中有以下记录
它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录上的索引也锁定范围内的索引。例如一个索引包含以下值10, 11, 13, and 20那么就需要锁定以下区间
```sql
| id | last_name | first_name | age |
|------|-------------|--------------|-------|
| 4 | stark | tony | 21 |
| 1 | tom | hiddleston | 30 |
| 3 | morgan | freeman | 40 |
| 5 | jeff | dean | 50 |
| 2 | donald | trump | 80 |
+------|-------------|--------------|-------+
```
那么就需要锁定以下范围:
```sql
(-∞, 21]
(21, 30]
(30, 40]
(40, 50]
(50, 80]
(80, ∞)
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
```
# 七、关系数据库设计理论

View File

@ -1623,10 +1623,10 @@ private List<Key> keys(Node x, Key l, Key h) {
## 2-3 查找树
<div align="center"> <img src="../pics//ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png" width="250"/> </div><br>
2-3 查找树引入了 2- 节点和 3- 节点,目的是为了让树平衡。一颗完美平衡的 2-3 查找树的所有空链接到根节点的距离应该是相同的。
<div align="center"> <img src="../pics//ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png" width="250"/> </div><br>
### 1. 插入操作
插入操作和 BST 的插入操作有很大区别BST 的插入操作是先进行一次未命中的查找,然后再将节点插入到对应的空链接上。但是 2-3 查找树如果也这么做的话,那么就会破坏了平衡性。它是将新节点插入到叶子节点上。
@ -2042,24 +2042,26 @@ public class SparseVector {
## 汉诺塔
<div align="center"> <img src="../pics//54f1e052-0596-4b5e-833c-e80d75bf3f9b.png" width="300"/> </div><br>
这是一个经典的递归问题,分为三步求解:
1. 将 n-1 个圆盘从 from -> buffer
2. 将 1 个圆盘从 from -> to
3. 将 n-1 个圆盘从 buffer -> to
- 将 n-1 个圆盘从 from -> buffer
<div align="center"> <img src="../pics//8587132a-021d-4f1f-a8ec-5a9daa7157a7.png" width="300"/> </div><br>
- 将 1 个圆盘从 from -> to
<div align="center"> <img src="../pics//2861e923-4862-4526-881c-15529279d49c.png" width="300"/> </div><br>
- 将 n-1 个圆盘从 buffer -> to
<div align="center"> <img src="../pics//1c4e8185-8153-46b6-bd5a-288b15feeae6.png" width="300"/> </div><br>
如果只有一个圆盘,那么只需要进行一次移动操作。
从上面的讨论可以知道a<sub>n</sub> = 2 * a<sub>n-1</sub> + 1显然 a<sub>n</sub> = 2<sup>n</sup> - 1n 个圆盘需要移动 2<sup>n</sup> - 1 次。
<div align="center"> <img src="../pics//54f1e052-0596-4b5e-833c-e80d75bf3f9b.png" width="300"/> </div><br>
<div align="center"> <img src="../pics//8587132a-021d-4f1f-a8ec-5a9daa7157a7.png" width="300"/> </div><br>
<div align="center"> <img src="../pics//2861e923-4862-4526-881c-15529279d49c.png" width="300"/> </div><br>
<div align="center"> <img src="../pics//1c4e8185-8153-46b6-bd5a-288b15feeae6.png" width="300"/> </div><br>
```java
public class Hanoi {
public static void move(int n, String from, String buffer, String to) {
@ -2105,7 +2107,7 @@ from H1 to H3
生成编码时,从根节点出发,向左遍历则添加二进制位 0向右则添加二进制位 1直到遍历到根节点根节点代表的字符的编码就是这个路径编码。
<div align="center"> <img src="../pics//3ff4f00a-2321-48fd-95f4-ce6001332151.png"/> </div><br>
<div align="center"> <img src="../pics//3ff4f00a-2321-48fd-95f4-ce6001332151.png" width="400"/> </div><br>
```java
public class Huffman {

View File

@ -79,7 +79,7 @@
实现可扩展主要有两种方式:
- 使用消息队列进行解耦,应用之间通过消息传递的方式进行通信;
- 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以过调用可复用的服务来实现业务逻辑,对其它产品没有影响。
- 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以过调用可复用的服务来实现业务逻辑,对其它产品没有影响。
# 四、可用性

View File

@ -98,7 +98,9 @@ public static synchronized Singleton getUniqueInstance() {
(三)饿汉式-线程安全
线程不安全问题主要是由于 uniqueInstance 被实例化了多次,如果 uniqueInstance 采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全问题。但是直接实例化的方式也丢失了延迟实例化带来的节约资源的优势。
线程不安全问题主要是由于 uniqueInstance 被实例化了多次,如果 uniqueInstance 采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全问题。
但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。
```java
private static Singleton uniqueInstance = new Singleton();
@ -106,7 +108,7 @@ private static Singleton uniqueInstance = new Singleton();
(四)双重校验锁-线程安全
uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行。也就是说,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
@ -131,7 +133,7 @@ public class Singleton {
}
```
考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程同时执行 if 语句,那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,也就是说会进行两次实例化,从而产生了两个实例。因此必须使用双重校验锁,也就是需要使用两个 if 语句。
考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程同时执行 if 语句,那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化,从而产生了两个实例。因此必须使用双重校验锁,也就是需要使用两个 if 语句。
```java
if (uniqueInstance == null) {
@ -157,7 +159,7 @@ uniqueInstance 采用 volatile 关键字修饰也是很有必要的。`uniqueIns
这种方式不仅具有延迟初始化的好处,而且由虚拟机提供了对线程安全的支持。
```source-java
```java
public class Singleton {
private Singleton() {
@ -299,7 +301,7 @@ public class Client {
### 意图
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化推迟到子类。
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
### 类图
@ -1851,11 +1853,7 @@ No gumball dispensed
### 与状态模式的比较
状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。
但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。
所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
@ -1969,7 +1967,7 @@ public abstract class CaffeineBeverage {
```
```java
public class Coffee extends CaffeineBeverage{
public class Coffee extends CaffeineBeverage {
@Override
void brew() {
System.out.println("Coffee.brew");
@ -1983,7 +1981,7 @@ public class Coffee extends CaffeineBeverage{
```
```java
public class Tea extends CaffeineBeverage{
public class Tea extends CaffeineBeverage {
@Override
void brew() {
System.out.println("Tea.brew");
@ -2238,7 +2236,7 @@ Number of items: 6
### 意图
使用什么都不做的空对象来代 NULL。
使用什么都不做的空对象来代 NULL。
一个方法返回 NULL意味着方法的调用端需要去检查返回值是否是 NULL这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值而直接使用返回的对象那么就有可能抛出空指针异常。
@ -2393,7 +2391,7 @@ public abstract class TV {
```
```java
public class Sony extends TV{
public class Sony extends TV {
@Override
public void on() {
System.out.println("Sony.on()");
@ -2412,7 +2410,7 @@ public class Sony extends TV{
```
```java
public class RCA extends TV{
public class RCA extends TV {
@Override
public void on() {
System.out.println("RCA.on()");
@ -2551,9 +2549,6 @@ public abstract class Component {
```
```java
import java.util.ArrayList;
import java.util.List;
public class Composite extends Component {
private List<Component> child;
@ -2659,7 +2654,7 @@ Composite:root
### 类图
装饰者Decorator和具体组件ConcreteComponent都继承自组件Component具体组件的方法实现不需要依赖于其它对象而装饰者组合了一个组件这样它可以装饰其它装饰者或者具体组件。所谓装饰就是把这个装饰者套在被装饰上从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的这属于它的功能然后调用被装饰者的方法实现从而也保留了被装饰者的功能。可以看到具体组件应当是装饰层次的最低层因为只有具体组件的方法实现不需要依赖于其它对象。
装饰者Decorator和具体组件ConcreteComponent都继承自组件Component具体组件的方法实现不需要依赖于其它对象而装饰者组合了一个组件这样它可以装饰其它装饰者或者具体组件。所谓装饰就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。
<div align="center"> <img src="../pics//137c593d-0a9e-47b8-a9e6-b71f540b82dd.png"/> </div><br>
@ -2770,7 +2765,7 @@ public class Client {
### 实现
观看电影需要操作很多电器,使用外观模式可以实现一键看电影功能。
观看电影需要操作很多电器,使用外观模式实现一键看电影功能。
```java
public class SubSystem {
@ -2811,7 +2806,7 @@ public class Client {
### 设计原则
最少知识原则:只和你的密友谈话。也就是客户对象所需要交互的对象应当尽可能少。
最少知识原则:只和你的密友谈话。也就是客户对象所需要交互的对象应当尽可能少。
## 6. 享元Flyweight
@ -2822,8 +2817,8 @@ public class Client {
### 类图
- Flyweight享元对象
- IntrinsicState内部状态相同的项元对象共享
- ExtrinsicState外部状态
- IntrinsicState内部状态享元对象共享内部状态
- ExtrinsicState外部状态,每个享元对象的外部状态不同
<div align="center"> <img src="../pics//d52270b4-9097-4667-9f18-f405fc661c99.png"/> </div><br>
@ -2854,8 +2849,6 @@ public class ConcreteFlyweight implements Flyweight {
```
```java
import java.util.HashMap;
public class FlyweightFactory {
private HashMap<String, Flyweight> flyweights = new HashMap<>();

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB