Merge pull request #1 from CyC2018/master

11
This commit is contained in:
echo6106 2018-04-12 16:49:19 +08:00 committed by GitHub
commit f6587585ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 56 additions and 65 deletions

View File

@ -803,7 +803,7 @@ DELETE /idX/delete HTTP/1.1 -> Returns 404
2. HTTP/1.1 支持管线化处理 2. HTTP/1.1 支持管线化处理
3. HTTP/1.1 支持虚拟主机 3. HTTP/1.1 支持虚拟主机
4. HTTP/1.1 新增状态码 100 4. HTTP/1.1 新增状态码 100
5. HTTP/1.1 只是分块传输编码 5. HTTP/1.1 支持分块传输编码
6. HTTP/1.1 新增缓存处理指令 max-age 6. HTTP/1.1 新增缓存处理指令 max-age
具体内容见上文 具体内容见上文
@ -828,7 +828,7 @@ HTTP/1.1 的解析是基于文本的,而 HTTP/2.0 采用二进制格式。
# 参考资料 # 参考资料
- 上野宣. 图解 HTTP[M]. Ren min you dian chu ban she, 2014. - 上野宣. 图解 HTTP[M]. 人民邮电出版社, 2014.
- [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP) - [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP)
- [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/) - [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/)
- [HTTP (HyperText Transfer Protocol)](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html) - [HTTP (HyperText Transfer Protocol)](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html)

View File

@ -44,11 +44,11 @@ java.awt.Desktop#getDesktop()
## 2. 简单工厂模式 ## 2. 简单工厂模式
在不对用户暴露对象内部逻辑的前提下创建对象;使用通用的接口来创建对象; 在不对用户暴露对象内部逻辑的前提下创建对象
## 3. 工厂方法模式 ## 3. 工厂方法模式
定义创建对象的接口,但是让子类来决定应该使用哪个类来创建;使用通用的接口来创建对象; 定义创建对象的接口,但是让子类来决定应该使用哪个类来创建
```java ```java
java.lang.Proxy#newProxyInstance() java.lang.Proxy#newProxyInstance()
@ -120,7 +120,7 @@ javax.swing.Action
## 3. 解释器模式 ## 3. 解释器模式
为语言创建解释器,通常由语言的语法和语法分析来定义。 为语言创建解释器,通常由语言的语法和语法分析来定义。
```java ```java
java.util.Pattern java.util.Pattern
java.text.Normalizer java.text.Normalizer
@ -229,7 +229,7 @@ JDBC
## 3. 组合模式 ## 3. 组合模式
将对象组合成树形结构来表示整-部分层次关系,允许用户以相同的方式处理单独对象和组合对象。 将对象组合成树形结构来表示整-部分层次关系,允许用户以相同的方式处理单独对象和组合对象。
```java ```java
javax.swing.JComponent#add(Component) javax.swing.JComponent#add(Component)

View File

@ -12,7 +12,7 @@
* [访问权限](#访问权限) * [访问权限](#访问权限)
* [抽象类与接口](#抽象类与接口) * [抽象类与接口](#抽象类与接口)
* [super](#super) * [super](#super)
* [重载与重写](#重载与重写) * [覆盖与重载](#覆盖与重载)
* [五、String](#五string) * [五、String](#五string)
* [String, StringBuffer and StringBuilder](#string,-stringbuffer-and-stringbuilder) * [String, StringBuffer and StringBuilder](#string,-stringbuffer-and-stringbuilder)
* [String 不可变的原因](#string-不可变的原因) * [String 不可变的原因](#string-不可变的原因)
@ -55,7 +55,7 @@ y.a = 1;
声明方法不能被子类覆盖。 声明方法不能被子类覆盖。
private 方法隐式地被指定为 final如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是覆盖基类方法,而是重载了 private 方法隐式地被指定为 final如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是覆盖基类方法,而是在子类中定义了一个新的方法
**3. 类** **3. 类**
@ -255,9 +255,9 @@ public class EqualExample {
## hashCode() ## hashCode()
hasCode() 返回散列值,而 equals() 是用来判断两个实例是否等。等的两个实例散列值一定要相同,但是散列值相同的两个实例不一定等。 hasCode() 返回散列值,而 equals() 是用来判断两个实例是否等。等的两个实例散列值一定要相同,但是散列值相同的两个实例不一定等
在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证相等的两个实例散列值也等。 在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证相等的两个实例散列值也等
下面的代码中,新建了两个等价的实例,并将它们添加到 HashSet 中。我们希望将这两个实例当成一样的,只在集合中添加一个实例,但是因为 EqualExample 没有实现 hasCode() 方法,因此这两个实例的散列值是不同的,最终导致集合添加了两个等价的实例。 下面的代码中,新建了两个等价的实例,并将它们添加到 HashSet 中。我们希望将这两个实例当成一样的,只在集合中添加一个实例,但是因为 EqualExample 没有实现 hasCode() 方法,因此这两个实例的散列值是不同的,最终导致集合添加了两个等价的实例。
@ -313,7 +313,7 @@ ToStringExample@4554617c
**1. cloneable** **1. cloneable**
clone() 是 Object 的受保护方法,这意味着,如果一个类不显式去重载 clone() 就没有这个方法。 clone() 是 Object 的受保护方法,这意味着,如果一个类不显式去覆盖 clone() 就没有这个方法。
```java ```java
public class CloneExample { public class CloneExample {
@ -327,7 +327,7 @@ CloneExample e1 = new CloneExample();
CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object' CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object'
``` ```
接下来重载 Object 的 clone() 得到以下实现: 接下来覆盖 Object 的 clone() 得到以下实现:
```java ```java
public class CloneExample { public class CloneExample {
@ -595,7 +595,7 @@ ac2.func1();
从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。 从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。
接口也可以包含域,并且这些域隐式都是 static 和 final 的。 接口也可以包含字段,并且这些字段隐式都是 static 和 final 的。
接口中的方法默认都是 public 的,并且不允许定义为 private 或者 protected。 接口中的方法默认都是 public 的,并且不允许定义为 private 或者 protected。
@ -636,7 +636,7 @@ System.out.println(InterfaceExample.x);
- 从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求子类和父类具有 IS-A 关系; - 从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求子类和父类具有 IS-A 关系;
- 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。 - 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。
- 接口的只能是 static 和 final 类型的,而抽象类的域可以有多种访问权限。 - 接口的字段只能是 static 和 final 类型的,而抽象类的域可以有多种访问权限。
- 接口的方法只能是 public 的,而抽象类的方法可以由多种访问权限。 - 接口的方法只能是 public 的,而抽象类的方法可以由多种访问权限。
**4. 使用选择** **4. 使用选择**
@ -706,11 +706,11 @@ SuperExtendExample.func()
> [Using the Keyword super](https://docs.oracle.com/javase/tutorial/java/IandI/super.html) > [Using the Keyword super](https://docs.oracle.com/javase/tutorial/java/IandI/super.html)
## 重载与重写 ## 覆盖与重载
- 重写存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法; - 覆盖Override存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法;
- 重载即存在于继承体系中,也存在于同一个类中,指一个方法与已经存在的方法或者父类的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。应该注意的是,返回值不同,其它都相同不算是重载。 - 重载Overload存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。应该注意的是,返回值不同,其它都相同不算是重载。
# 五、String # 五、String
@ -739,7 +739,7 @@ SuperExtendExample.func()
如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。 如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
<div align="center"> <img src="../pics//f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg"/> </div><br> <div align="center"> <img src="../pics//f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg" width=""/> </div><br>
**3. 安全性** **3. 安全性**
@ -773,7 +773,7 @@ String s5 = "bbb";
System.out.println(s4 == s5); // true System.out.println(s4 == s5); // true
``` ```
Java 虚拟机将堆划分成新生代、老年代和永久代PermGen Space。在 Java 6 之前,字符串常量池被放在永久代中,而在 Java 7,它被放在堆的其它位置。这是因为永久代的空间有限,如果大量使用字符串的场景下会导致 OutOfMemoryError 错误。 Java 虚拟机将堆划分成新生代、老年代和永久代PermGen Space。在 Java 7 之前,字符串常量池被放在永久代中,而在 Java 7它被放在堆的其它位置。这是因为永久代的空间有限如果大量使用字符串的场景下会导致 OutOfMemoryError 错误。
> [What is String interning?](https://stackoverflow.com/questions/10578984/what-is-string-interning) </br> [深入解析 String#intern](https://tech.meituan.com/in_depth_understanding_string_intern.html) > [What is String interning?](https://stackoverflow.com/questions/10578984/what-is-string-interning) </br> [深入解析 String#intern](https://tech.meituan.com/in_depth_understanding_string_intern.html)
@ -946,7 +946,7 @@ Throwable 可以用来表示任何可以作为异常抛出的类,分为两种
1. **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复; 1. **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;
2. **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception此时程序奔溃并且无法恢复。 2. **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception此时程序奔溃并且无法恢复。
<div align="center"> <img src="../pics//PPjwP.png"/> </div><br> <div align="center"> <img src="../pics//PPjwP.png" width="600"/> </div><br>
> [Java 入门之异常处理](https://www.tianmaying.com/tutorial/Java-Exception) </br> [Java 异常的面试问题及答案 -Part 1](http://www.importnew.com/7383.html) > [Java 入门之异常处理](https://www.tianmaying.com/tutorial/Java-Exception) </br> [Java 异常的面试问题及答案 -Part 1](http://www.importnew.com/7383.html)

View File

@ -567,7 +567,7 @@ static final int DEFAULT_CONCURRENCY_LEVEL = 16;
<div align="center"> <img src="../pics//image005.jpg"/> </div><br> <div align="center"> <img src="../pics//image005.jpg"/> </div><br>
### 2. HashEntery 的不可变性 ### 2. HashEntry 的不可变性
HashEntry 中的 keyhashnext 都声明为 final 型。这意味着,不能把节点添加到链接的中间和尾部,也不能在链接的中间和尾部删除节点。这个特性可以保证:在访问某个节点时,这个节点之后的链接不会被改变。这个特性可以大大降低处理链表时的复杂性。 HashEntry 中的 keyhashnext 都声明为 final 型。这意味着,不能把节点添加到链接的中间和尾部,也不能在链接的中间和尾部删除节点。这个特性可以保证:在访问某个节点时,这个节点之后的链接不会被改变。这个特性可以大大降低处理链表时的复杂性。

View File

@ -76,7 +76,7 @@
## 无限期等待Waiting ## 无限期等待Waiting
等待其它线程显地唤醒,否则不会被分配 CPU 时间片。 等待其它线程显地唤醒,否则不会被分配 CPU 时间片。
| 进入方法 | 退出方法 | | 进入方法 | 退出方法 |
| --- | --- | | --- | --- |
@ -794,9 +794,10 @@ public class SemaphoreExample {
try { try {
semaphore.acquire(); semaphore.acquire();
System.out.print(semaphore.availablePermits() + " "); System.out.print(semaphore.availablePermits() + " ");
semaphore.release();
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} finally {
semaphore.release();
} }
}); });
} }
@ -1413,7 +1414,7 @@ ABA :如果一个变量 V 初次读取的时候是 A 值,它的值被改成
**(二)栈封闭** **(二)栈封闭**
多个线程方法同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在栈中,属于线程私有的。 多个线程访问同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在栈中,属于线程私有的。
```java ```java
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;

View File

@ -40,7 +40,7 @@ InnoDB 是 MySQL 默认的事务型存储引擎,只有在需要 InnoDB 不支
## MyISAM ## MyISAM
MyISAM 提供了大量的特性包括全文索引、压缩、空间函数GIS等。但 MyISAM 不支持事务和行级锁,而且崩溃后无法安全恢复。 MyISAM 提供了大量的特性包括全文索引、压缩、空间函数GIS等。但 MyISAM 不支持事务和行级锁,而且崩溃后无法安全恢复。应该注意的是MySQL5.6.4 添加了对 InnoDB 引擎的全文索引支持。
只能对整张表加锁,而不是针对行。 只能对整张表加锁,而不是针对行。
@ -62,7 +62,7 @@ MyISAM 设计简单,数据以紧密格式存储,所以在某些场景下性
2. 备份InnoDB 支持在线热备份。 2. 备份InnoDB 支持在线热备份。
3. 崩溃恢复MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。 3. 崩溃恢复MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。
4. 并发MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。 4. 并发MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。
5. 其它特性MyISAM 支持全文索引,地理空间索引。 5. 其它特性MyISAM 支持,地理空间索引。
# 二、数据类型 # 二、数据类型

View File

@ -95,7 +95,7 @@
**一致性哈希** **一致性哈希**
Distributed Hash TableDHT对于哈希空间 0\~2<sup>n</sup>,将该哈希空间看成一个哈希环,将每个节点都配置到哈希环上。每个数据对象通过哈希取模得到哈希值之后,存放到哈希环中顺时针方向第一个大于等于该哈希值的节点上。 Distributed Hash TableDHT对于哈希空间 [0, 2<sup>n</sup>-1],将该哈希空间看成一个哈希环,将每个节点都配置到哈希环上。每个数据对象通过哈希取模得到哈希值之后,存放到哈希环中顺时针方向第一个大于等于该哈希值的节点上。
<div align="center"> <img src="../pics//d2d34239-e7c1-482b-b33e-3170c5943556.jpg"/> </div><br> <div align="center"> <img src="../pics//d2d34239-e7c1-482b-b33e-3170c5943556.jpg"/> </div><br>

View File

@ -82,7 +82,7 @@
# 2. 实现 Singleton # 2. 实现 Singleton
> [单例模式](https://github.com/CyC2018/Interview- Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md) > [单例模式](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md)
# 3. 数组中重复的数字 # 3. 数组中重复的数字
@ -1142,7 +1142,7 @@ public ListNode Merge(ListNode list1, ListNode list2) {
ListNode head = new ListNode(-1); ListNode head = new ListNode(-1);
ListNode cur = head; ListNode cur = head;
while (list1 != null && list2 != null) { while (list1 != null && list2 != null) {
if (list1.val < list2.val) { if (list1.val <= list2.val) {
cur.next = list1; cur.next = list1;
list1 = list1.next; list1 = list1.next;
} else { } else {
@ -1409,7 +1409,7 @@ public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
## 题目描述 ## 题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。假设输入的数组的任意两个数字都互不相同。
例如,下图是后序遍历序列 3,1,2 所对应的二叉搜索树。 例如,下图是后序遍历序列 3,1,2 所对应的二叉搜索树。
@ -1673,36 +1673,30 @@ public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
int kthSmallest = findKthSmallest(nums, k - 1); int kthSmallest = findKthSmallest(nums, k - 1);
ArrayList<Integer> ret = new ArrayList<>(); ArrayList<Integer> ret = new ArrayList<>();
for (int val : nums) { for (int val : nums) {
if (val <= kthSmallest && ret.size() < k) ret.add(val); if (val <= kthSmallest && ret.size() < k) {
ret.add(val);
}
} }
return ret; return ret;
} }
public int findKthSmallest(int[] nums, int k) { public int findKthSmallest(int[] nums, int k) {
int l = 0; int l = 0, h = nums.length - 1;
int h = nums.length - 1;
while (l < h) { while (l < h) {
int j = partition(nums, l, h); int j = partition(nums, l, h);
if (j < k) { if (j == k) break;
l = j + 1; if (j > k) h = j - 1;
} else if (j > k) { else l = j + 1;
h = j - 1;
} else {
break;
}
} }
return nums[k]; return nums[k];
} }
private int partition(int[] nums, int l, int h) { private int partition(int[] nums, int l, int h) {
int i = l; int i = l, j = h + 1;
int j = h + 1;
while (true) { while (true) {
while (i < h && nums[++i] < nums[l]) ; while (i < h && nums[++i] < nums[l]) ;
while (j > l && nums[l] < nums[--j]) ; while (j > l && nums[l] < nums[--j]) ;
if (i >= j) { if (i >= j) break;
break;
}
swap(nums, i, j); swap(nums, i, j);
} }
swap(nums, l, j); swap(nums, l, j);
@ -1710,9 +1704,7 @@ private int partition(int[] nums, int l, int h) {
} }
private void swap(int[] nums, int i, int j) { private void swap(int[] nums, int i, int j) {
int t = nums[i]; int t = nums[i]; nums[i] = nums[j]; nums[j] = t;
nums[i] = nums[j];
nums[j] = t;
} }
``` ```
@ -1882,7 +1874,7 @@ private int getAmountOfDigit(int digit) {
} }
/** /**
* 在 digit 位数组成的字符串中,第 index 为的 * 在 digit 位数组成的字符串中,第 index
*/ */
private int digitAtIndex(int index, int digit) { private int digitAtIndex(int index, int digit) {
int number = beginNumber(digit) + index / digit; int number = beginNumber(digit) + index / digit;
@ -1994,6 +1986,7 @@ public int longestSubStringWithoutDuplication(String str) {
int curLen = 0; int curLen = 0;
int maxLen = 0; int maxLen = 0;
int[] indexs = new int[26]; int[] indexs = new int[26];
Arrays.fill(indexs, -1);
for (int i = 0; i < str.length(); i++) { for (int i = 0; i < str.length(); i++) {
int c = str.charAt(i) - 'a'; int c = str.charAt(i) - 'a';
int preIndex = indexs[c]; int preIndex = indexs[c];
@ -2018,21 +2011,19 @@ public int longestSubStringWithoutDuplication(String str) {
## 解题思路 ## 解题思路
```java ```java
public int GetUglyNumber_Solution(int N) { public int GetUglyNumber_Solution(int index) {
if (N <= 6) return N; if (index <= 6) return index;
int i2 = 0, i3 = 0, i5 = 0; int i2 = 0, i3 = 0, i5 = 0;
int cnt = 1; int[] dp = new int[index];
int[] dp = new int[N];
dp[0] = 1; dp[0] = 1;
while (cnt < N) { for (int i = 1; i < index; i++) {
int n2 = dp[i2] * 2, n3 = dp[i3] * 3, n5 = dp[i5] * 5; int n2 = dp[i2] * 2, n3 = dp[i3] * 3, n5 = dp[i5] * 5;
int min = Math.min(n2, Math.min(n3, n5)); dp[i] = Math.min(n2, Math.min(n3, n5));
dp[cnt++] = min; if (dp[i] == n2) i2++;
if (min == n2) i2++; if (dp[i] == n3) i3++;
if (min == n3) i3++; if (dp[i] == n5) i5++;
if (min == n5) i5++;
} }
return dp[N - 1]; return dp[index - 1];
} }
``` ```

View File

@ -307,7 +307,7 @@ InnoDB 的 MVCC 使用到的快照存储在 Undo 日志中,该日志通过回
### 4. UPDATE ### 4. UPDATE
将系统版本号作为更新后的数据行快照的创建版本号,同时将系统版本号作为作为更新前的数据行快照的删除版本号。可以理解为先执行 DELETE 后执行 INSERT。 将系统版本号作为更新后的数据行快照的创建版本号,同时将系统版本号作为更新前的数据行快照的删除版本号。可以理解为先执行 DELETE 后执行 INSERT。
## 快照读与当前读 ## 快照读与当前读
@ -401,7 +401,7 @@ SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
1. 冗余数据,例如学生-2 出现了两次。 1. 冗余数据,例如学生-2 出现了两次。
2. 修改异常,修改了一个记录中的信息,但是另一个记录中相同的信息却没有被修改。 2. 修改异常,修改了一个记录中的信息,但是另一个记录中相同的信息却没有被修改。
3. 删除异常,删除一个信息,那么也会丢失其它信息。例如如果删除了课程-1需要删除第行和第三行,那么学生-1 的信息就会丢失。 3. 删除异常,删除一个信息,那么也会丢失其它信息。例如如果删除了课程-1需要删除第行和第三行,那么学生-1 的信息就会丢失。
4. 插入异常,例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。 4. 插入异常,例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。
## 范式 ## 范式

View File

@ -1439,7 +1439,7 @@ public class Transaction{
### 3. 基于线性探测法的散列表 ### 3. 基于线性探测法的散列表
线性探测法使用空位来解决冲突,当冲突发生时,向前探测一个空位来存储冲突的键。使用线探测法,数组的大小 M 应当大于键的个数 NM>N)。 线性探测法使用空位来解决冲突,当冲突发生时,向前探测一个空位来存储冲突的键。使用线探测法,数组的大小 M 应当大于键的个数 NM>N)。
<div align="center"> <img src="../pics//dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png" width="400"/> </div><br> <div align="center"> <img src="../pics//dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png" width="400"/> </div><br>

View File

@ -276,13 +276,12 @@ public class ConcreteFactory2 extends Factory {
抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂模式只是用于创建一个对象,这和抽象工厂模式有很大不同。 抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂模式只是用于创建一个对象,这和抽象工厂模式有很大不同。
抽象工厂模式用到了工厂模式来创建单一对象,在类图左部,AbstractFactory 中的 createProductA 和 createProductB 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂模式的定义。 抽象工厂模式用到了工厂模式来创建单一对象AbstractFactory 中的 createProductA 和 createProductB 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂模式的定义。
至于创建对象的家族这一概念是在 Client 体现Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象在这里这两个对象就有很大的相关性Client 需要同时创建出这两个对象。 至于创建对象的家族这一概念是在 Client 体现Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象在这里这两个对象就有很大的相关性Client 需要同时创建出这两个对象。
从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory而工厂模式使用了继承。 从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory而工厂模式使用了继承。
## 代码实现 ## 代码实现
```java ```java